@midscene/core 1.16.0 → 1.16.2

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 (147) hide show
  1. package/dist/es/agent/agent-options.mjs +36 -0
  2. package/dist/es/agent/agent-options.mjs.map +1 -0
  3. package/dist/es/agent/agent-report-manager.mjs +323 -0
  4. package/dist/es/agent/agent-report-manager.mjs.map +1 -0
  5. package/dist/es/agent/agent.mjs +112 -228
  6. package/dist/es/agent/agent.mjs.map +1 -1
  7. package/dist/es/agent/cache-config.mjs +21 -1
  8. package/dist/es/agent/cache-config.mjs.map +1 -1
  9. package/dist/es/agent/tasks.mjs +3 -1
  10. package/dist/es/agent/tasks.mjs.map +1 -1
  11. package/dist/es/agent/utils.mjs +4 -4
  12. package/dist/es/agent/utils.mjs.map +1 -1
  13. package/dist/es/ai-model/models/auto-glm/parser.mjs +1 -1
  14. package/dist/es/ai-model/models/auto-glm/parser.mjs.map +1 -1
  15. package/dist/es/ai-model/models/kimi.mjs +1 -1
  16. package/dist/es/ai-model/models/kimi.mjs.map +1 -1
  17. package/dist/es/ai-model/service-caller/call-ai.mjs +10 -1
  18. package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
  19. package/dist/es/ai-model/service-caller/openai/call-openai.mjs +7 -2
  20. package/dist/es/ai-model/service-caller/openai/call-openai.mjs.map +1 -1
  21. package/dist/es/ai-model/shared/model-locate-result/errors.mjs +1 -1
  22. package/dist/es/ai-model/shared/model-locate-result/errors.mjs.map +1 -1
  23. package/dist/es/ai-model/shared/model-locate-result/parse.mjs +6 -2
  24. package/dist/es/ai-model/shared/model-locate-result/parse.mjs.map +1 -1
  25. package/dist/es/device/index.mjs +9 -8
  26. package/dist/es/device/index.mjs.map +1 -1
  27. package/dist/es/report-generator.mjs +76 -36
  28. package/dist/es/report-generator.mjs.map +1 -1
  29. package/dist/es/report-html-template.mjs +1 -1
  30. package/dist/es/test-run-report.mjs.map +1 -1
  31. package/dist/es/test-runner/engine/document-runtime.mjs +30 -24
  32. package/dist/es/test-runner/engine/document-runtime.mjs.map +1 -1
  33. package/dist/es/test-runner/engine/project-runtime.mjs +17 -11
  34. package/dist/es/test-runner/engine/project-runtime.mjs.map +1 -1
  35. package/dist/es/test-runner/engine/resource-operations.mjs +33 -8
  36. package/dist/es/test-runner/engine/resource-operations.mjs.map +1 -1
  37. package/dist/es/test-runner/engine/run-collected-case.mjs +1 -0
  38. package/dist/es/test-runner/engine/run-collected-case.mjs.map +1 -1
  39. package/dist/es/test-runner/engine/run-workflow-document.mjs +3 -0
  40. package/dist/es/test-runner/engine/run-workflow-document.mjs.map +1 -1
  41. package/dist/es/test-runner/engine/setup-timeout.mjs +37 -0
  42. package/dist/es/test-runner/engine/setup-timeout.mjs.map +1 -0
  43. package/dist/es/test-runner/reporting/merge-report.mjs +3 -1
  44. package/dist/es/test-runner/reporting/merge-report.mjs.map +1 -1
  45. package/dist/es/test-runner/reporting/metrics.mjs +39 -2
  46. package/dist/es/test-runner/reporting/metrics.mjs.map +1 -1
  47. package/dist/es/utils.mjs +1 -1
  48. package/dist/es/yaml/execution-session.mjs +2 -1
  49. package/dist/es/yaml/execution-session.mjs.map +1 -1
  50. package/dist/es/yaml/legacy-yaml-report.mjs +1 -1
  51. package/dist/es/yaml/legacy-yaml-report.mjs.map +1 -1
  52. package/dist/es/yaml/player.mjs +17 -3
  53. package/dist/es/yaml/player.mjs.map +1 -1
  54. package/dist/lib/agent/agent-options.js +73 -0
  55. package/dist/lib/agent/agent-options.js.map +1 -0
  56. package/dist/lib/agent/agent-report-manager.js +357 -0
  57. package/dist/lib/agent/agent-report-manager.js.map +1 -0
  58. package/dist/lib/agent/agent.js +110 -226
  59. package/dist/lib/agent/agent.js.map +1 -1
  60. package/dist/lib/agent/cache-config.js +23 -0
  61. package/dist/lib/agent/cache-config.js.map +1 -1
  62. package/dist/lib/agent/tasks.js +3 -1
  63. package/dist/lib/agent/tasks.js.map +1 -1
  64. package/dist/lib/agent/utils.js +2 -2
  65. package/dist/lib/agent/utils.js.map +1 -1
  66. package/dist/lib/ai-model/models/auto-glm/parser.js +1 -1
  67. package/dist/lib/ai-model/models/auto-glm/parser.js.map +1 -1
  68. package/dist/lib/ai-model/models/kimi.js +1 -1
  69. package/dist/lib/ai-model/models/kimi.js.map +1 -1
  70. package/dist/lib/ai-model/service-caller/call-ai.js +10 -1
  71. package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
  72. package/dist/lib/ai-model/service-caller/openai/call-openai.js +7 -2
  73. package/dist/lib/ai-model/service-caller/openai/call-openai.js.map +1 -1
  74. package/dist/lib/ai-model/shared/model-locate-result/errors.js +1 -1
  75. package/dist/lib/ai-model/shared/model-locate-result/errors.js.map +1 -1
  76. package/dist/lib/ai-model/shared/model-locate-result/parse.js +6 -2
  77. package/dist/lib/ai-model/shared/model-locate-result/parse.js.map +1 -1
  78. package/dist/lib/device/index.js +9 -8
  79. package/dist/lib/device/index.js.map +1 -1
  80. package/dist/lib/report-generator.js +75 -35
  81. package/dist/lib/report-generator.js.map +1 -1
  82. package/dist/lib/report-html-template.js +1 -1
  83. package/dist/lib/test-run-report.js.map +1 -1
  84. package/dist/lib/test-runner/engine/document-runtime.js +30 -24
  85. package/dist/lib/test-runner/engine/document-runtime.js.map +1 -1
  86. package/dist/lib/test-runner/engine/project-runtime.js +17 -11
  87. package/dist/lib/test-runner/engine/project-runtime.js.map +1 -1
  88. package/dist/lib/test-runner/engine/resource-operations.js +36 -8
  89. package/dist/lib/test-runner/engine/resource-operations.js.map +1 -1
  90. package/dist/lib/test-runner/engine/run-collected-case.js +1 -0
  91. package/dist/lib/test-runner/engine/run-collected-case.js.map +1 -1
  92. package/dist/lib/test-runner/engine/run-workflow-document.js +3 -0
  93. package/dist/lib/test-runner/engine/run-workflow-document.js.map +1 -1
  94. package/dist/lib/test-runner/engine/setup-timeout.js +71 -0
  95. package/dist/lib/test-runner/engine/setup-timeout.js.map +1 -0
  96. package/dist/lib/test-runner/reporting/merge-report.js +3 -1
  97. package/dist/lib/test-runner/reporting/merge-report.js.map +1 -1
  98. package/dist/lib/test-runner/reporting/metrics.js +39 -2
  99. package/dist/lib/test-runner/reporting/metrics.js.map +1 -1
  100. package/dist/lib/utils.js +1 -1
  101. package/dist/lib/yaml/execution-session.js +4 -0
  102. package/dist/lib/yaml/execution-session.js.map +1 -1
  103. package/dist/lib/yaml/legacy-yaml-report.js +1 -1
  104. package/dist/lib/yaml/legacy-yaml-report.js.map +1 -1
  105. package/dist/lib/yaml/player.js +16 -2
  106. package/dist/lib/yaml/player.js.map +1 -1
  107. package/dist/types/agent/agent-options.d.ts +4 -0
  108. package/dist/types/agent/agent-options.d.ts.map +1 -0
  109. package/dist/types/agent/agent-report-manager.d.ts +92 -0
  110. package/dist/types/agent/agent-report-manager.d.ts.map +1 -0
  111. package/dist/types/agent/agent.d.ts +31 -24
  112. package/dist/types/agent/agent.d.ts.map +1 -1
  113. package/dist/types/agent/cache-config.d.ts +10 -0
  114. package/dist/types/agent/cache-config.d.ts.map +1 -1
  115. package/dist/types/agent/tasks.d.ts +1 -0
  116. package/dist/types/agent/tasks.d.ts.map +1 -1
  117. package/dist/types/ai-model/models/auto-glm/parser.d.ts.map +1 -1
  118. package/dist/types/ai-model/service-caller/openai/call-openai.d.ts.map +1 -1
  119. package/dist/types/ai-model/shared/model-locate-result/errors.d.ts +1 -1
  120. package/dist/types/ai-model/shared/model-locate-result/errors.d.ts.map +1 -1
  121. package/dist/types/ai-model/shared/model-locate-result/parse.d.ts.map +1 -1
  122. package/dist/types/device/index.d.ts +7 -7
  123. package/dist/types/device/index.d.ts.map +1 -1
  124. package/dist/types/report-generator.d.ts +5 -1
  125. package/dist/types/report-generator.d.ts.map +1 -1
  126. package/dist/types/test-run-report.d.ts +4 -1
  127. package/dist/types/test-run-report.d.ts.map +1 -1
  128. package/dist/types/test-runner/engine/document-runtime.d.ts.map +1 -1
  129. package/dist/types/test-runner/engine/project-runtime.d.ts.map +1 -1
  130. package/dist/types/test-runner/engine/resource-operations.d.ts +5 -0
  131. package/dist/types/test-runner/engine/resource-operations.d.ts.map +1 -1
  132. package/dist/types/test-runner/engine/run-collected-case.d.ts.map +1 -1
  133. package/dist/types/test-runner/engine/run-workflow-document.d.ts.map +1 -1
  134. package/dist/types/test-runner/engine/setup-timeout.d.ts +3 -0
  135. package/dist/types/test-runner/engine/setup-timeout.d.ts.map +1 -0
  136. package/dist/types/test-runner/engine/types.d.ts +7 -0
  137. package/dist/types/test-runner/engine/types.d.ts.map +1 -1
  138. package/dist/types/test-runner/parser/types.d.ts +5 -0
  139. package/dist/types/test-runner/parser/types.d.ts.map +1 -1
  140. package/dist/types/test-runner/project.d.ts +22 -0
  141. package/dist/types/test-runner/project.d.ts.map +1 -1
  142. package/dist/types/test-runner/reporting/merge-report.d.ts.map +1 -1
  143. package/dist/types/test-runner/reporting/metrics.d.ts.map +1 -1
  144. package/dist/types/yaml/execution-session.d.ts +2 -0
  145. package/dist/types/yaml/execution-session.d.ts.map +1 -1
  146. package/dist/types/yaml/player.d.ts.map +1 -1
  147. package/package.json +2 -2
@@ -3,8 +3,8 @@ import { appendFile, readFile, rename, unlink, writeFile } from "node:fs/promise
3
3
  import { basename, dirname, join } from "node:path";
4
4
  import { getMidsceneRunSubDir } from "@midscene/shared/common";
5
5
  import { MIDSCENE_REPORT_QUIET, globalConfigManager } from "@midscene/shared/env";
6
- import { getDebug } from "@midscene/shared/logger";
7
- import { ifInBrowser, logMsg, uuid } from "@midscene/shared/utils";
6
+ import { getDebug, logReport } from "@midscene/shared/logger";
7
+ import { ifInBrowser, uuid } from "@midscene/shared/utils";
8
8
  import { DATA_SCREENSHOT_MODE_ATTR, generateAgentReportComment, generateDumpScriptTag, generateImageScriptTag, generateTestRunReportScriptTag, getBaseUrlFixScript } from "./dump/html-utils.mjs";
9
9
  import { imageRefFileExtension, normalizeStoredImageRef } from "./dump/image-reference.mjs";
10
10
  import { compactReportDumps } from "./dump/report-dump-compactor.mjs";
@@ -54,12 +54,12 @@ class ReportGenerator {
54
54
  });
55
55
  }
56
56
  onExecutionUpdate(execution, reportMeta, attributes) {
57
+ this.assertOpen();
57
58
  this.lastExecution = execution;
58
59
  this.lastReportMeta = reportMeta;
59
60
  this.executionsByKey.set(this.getExecutionCommentKey(execution), execution);
60
61
  this.mergeReportAttributes(attributes);
61
62
  this.writeQueue = this.writeQueue.then(async ()=>{
62
- if (this.destroyed) return;
63
63
  await this.doWriteExecution(execution, reportMeta);
64
64
  });
65
65
  }
@@ -67,22 +67,40 @@ class ReportGenerator {
67
67
  await this.writeQueue;
68
68
  }
69
69
  async finalize() {
70
- if (this.lastExecution && this.lastReportMeta) this.onExecutionUpdate(this.lastExecution, this.lastReportMeta);
71
- await this.flush();
72
- this.destroyed = true;
73
- if (!this.initialized) return;
74
- await this.appendAgentReportComment();
75
- try {
76
- await compactReportDumps(this.reportPath);
77
- } catch (error) {
78
- warnReport(`Failed to compact report ${this.reportPath}; keeping the uncompressed report: ${String(error)}`);
70
+ if (!this.finalization) {
71
+ if (this.lastExecution && this.lastReportMeta) this.onExecutionUpdate(this.lastExecution, this.lastReportMeta);
72
+ this.finalization = this.finishPublication();
79
73
  }
80
- return this.reportPath;
74
+ return this.finalization;
75
+ }
76
+ async finishPublication() {
77
+ await Promise.all([
78
+ this.flush(),
79
+ ...this.sourcePublications
80
+ ]);
81
+ if (this.initialized) {
82
+ await this.appendAgentReportComment();
83
+ try {
84
+ await compactReportDumps(this.reportPath);
85
+ } catch (error) {
86
+ warnReport(`Failed to compact report ${this.reportPath}; keeping the uncompressed report: ${String(error)}`);
87
+ }
88
+ }
89
+ this.executionsByKey.clear();
90
+ this.lastExecution = void 0;
91
+ this.lastReportMeta = void 0;
92
+ this.executionCommentKeyByObject = new WeakMap();
93
+ this.executionLogFileIndexByExecutionKey.clear();
94
+ return this.initialized ? this.reportPath : void 0;
95
+ }
96
+ assertOpen() {
97
+ if (this.finalization) throw new Error('The report has been finalized.');
81
98
  }
82
99
  getReportPath() {
83
100
  return this.reportPath;
84
101
  }
85
102
  async writeRunnerReport(input, scopeId, meta) {
103
+ this.assertOpen();
86
104
  this.writeQueue = this.writeQueue.then(async ()=>{
87
105
  const reportMeta = this.lastReportMeta ?? meta;
88
106
  const executions = [
@@ -176,6 +194,7 @@ ${generateAgentReportComment(agentDump)}`;
176
194
  };
177
195
  }
178
196
  async createSourceSnapshot(scopeId, executionIds) {
197
+ this.assertOpen();
179
198
  if (!scopeId || basename(scopeId) !== scopeId) throw new Error('Invalid report scope ID');
180
199
  const capturedAt = new Date().toISOString();
181
200
  const selectedIds = executionIds ? new Set(executionIds) : void 0;
@@ -191,27 +210,35 @@ ${generateAgentReportComment(agentDump)}`;
191
210
  },
192
211
  executions
193
212
  }).serialize());
194
- const { serialized } = await this.serializeSourceDump(frozen, screenshots);
195
- const dumpPath = `${this.reportPath}.${scopeId}.${uuid()}.json`;
196
- mkdirSync(dirname(dumpPath), {
197
- recursive: true
198
- });
199
- await writeFile(dumpPath, serialized, {
200
- flag: 'wx'
201
- });
202
- return {
203
- sourcePath: this.reportPath,
204
- dumpPath,
205
- collection: {
206
- status: 'complete',
207
- capturedAt
208
- }
209
- };
213
+ const publication = (async ()=>{
214
+ const { serialized } = await this.serializeSourceDump(frozen, screenshots);
215
+ const dumpPath = `${this.reportPath}.${scopeId}.${uuid()}.json`;
216
+ mkdirSync(dirname(dumpPath), {
217
+ recursive: true
218
+ });
219
+ await writeFile(dumpPath, serialized, {
220
+ flag: 'wx'
221
+ });
222
+ return {
223
+ sourcePath: this.reportPath,
224
+ dumpPath,
225
+ collection: {
226
+ status: 'complete',
227
+ capturedAt
228
+ }
229
+ };
230
+ })();
231
+ this.sourcePublications.add(publication);
232
+ try {
233
+ return await publication;
234
+ } finally{
235
+ this.sourcePublications.delete(publication);
236
+ }
210
237
  }
211
238
  printReportPath() {
212
239
  if (!this.autoPrint || !this.reportPath) return;
213
240
  if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET)) return;
214
- 'directory' === this.screenshotMode ? logMsg(`Midscene - report file updated: npx serve ${dirname(this.reportPath)}`) : logMsg(`Midscene - report file updated: ${this.reportPath}`);
241
+ 'directory' === this.screenshotMode ? logReport(`Midscene - report file updated: npx serve ${dirname(this.reportPath)}`) : logReport(`Midscene - report file updated: ${this.reportPath}`);
215
242
  }
216
243
  async doWriteExecution(execution, reportMeta) {
217
244
  const singleDump = this.wrapAsReportDump(execution, reportMeta);
@@ -230,8 +257,8 @@ ${generateAgentReportComment(agentDump)}`;
230
257
  if (!existsSync(this.reportPath)) return;
231
258
  this.initialized = true;
232
259
  if (!this.shouldPersistExecutionDump) return;
233
- const reportDir = dirname(this.reportPath);
234
- const existingExecutionIndices = readdirSync(reportDir).map((name)=>/^(\d+)\.execution\.json$/.exec(name)?.[1]).filter((index)=>Boolean(index)).map((index)=>Number.parseInt(index, 10)).filter((index)=>Number.isFinite(index));
260
+ if (!existsSync(this.executionDumpDir)) return;
261
+ const existingExecutionIndices = readdirSync(this.executionDumpDir).map((name)=>/^(\d+)\.execution\.json$/.exec(name)?.[1]).filter((index)=>Boolean(index)).map((index)=>Number.parseInt(index, 10)).filter((index)=>Number.isFinite(index));
235
262
  if (existingExecutionIndices.length > 0) this.executionLogIndex = Math.max(...existingExecutionIndices);
236
263
  }
237
264
  getDumpScriptAttributes() {
@@ -302,8 +329,11 @@ ${generateAgentReportComment(agentDump)}`;
302
329
  this.executionCommentKeyByObject.set(execution, key);
303
330
  return key;
304
331
  }
332
+ get executionDumpDir() {
333
+ return 'inline' === this.screenshotMode ? join(dirname(this.reportPath), '.execution-dumps', basename(this.reportPath)) : dirname(this.reportPath);
334
+ }
305
335
  async persistExecutionDumpToFile(execution, singleDump, referenceImageRefs) {
306
- const dir = dirname(this.reportPath);
336
+ const dir = this.executionDumpDir;
307
337
  if (!existsSync(dir)) mkdirSync(dir, {
308
338
  recursive: true
309
339
  });
@@ -315,8 +345,17 @@ ${generateAgentReportComment(agentDump)}`;
315
345
  this.executionLogFileIndexByExecutionKey.set(executionLogKey, fileIndex);
316
346
  }
317
347
  const fileName = `${fileIndex}.execution.json`;
318
- const filePath = join(dirname(this.reportPath), fileName);
319
- await writeFile(filePath, singleDump.serializeWithReferenceImages(referenceImageRefs, 2), 'utf-8');
348
+ const filePath = join(dir, fileName);
349
+ let serialized = singleDump.serializeWithReferenceImages(referenceImageRefs, 2);
350
+ if ('inline' === this.screenshotMode) serialized = JSON.stringify(JSON.parse(serialized), (_key, value)=>{
351
+ const ref = normalizeStoredImageRef(value);
352
+ return ref ? {
353
+ ...ref,
354
+ storage: 'file',
355
+ path: `../../screenshots/${ref.id}.${imageRefFileExtension(ref)}`
356
+ } : value;
357
+ }, 2);
358
+ await writeFile(filePath, serialized, 'utf-8');
320
359
  }
321
360
  constructor(options){
322
361
  _define_property(this, "reportPath", void 0);
@@ -337,7 +376,8 @@ ${generateAgentReportComment(agentDump)}`;
337
376
  _define_property(this, "reportAttributes", {});
338
377
  _define_property(this, "agentCommentWritten", false);
339
378
  _define_property(this, "writeQueue", Promise.resolve());
340
- _define_property(this, "destroyed", false);
379
+ _define_property(this, "finalization", void 0);
380
+ _define_property(this, "sourcePublications", new Set());
341
381
  this.reportPath = options.reportPath;
342
382
  this.screenshotMode = options.screenshotMode;
343
383
  this.shouldPersistExecutionDump = options.persistExecutionDump ?? false;
@@ -1 +1 @@
1
- {"version":3,"file":"report-generator.mjs","sources":["../../src/report-generator.ts"],"sourcesContent":["/*\n * PERF INVARIANT — DO NOT reintroduce sync fs APIs (writeFileSync /\n * appendFileSync) in this file's write paths. `ReportGenerator` runs on\n * the Electron main event loop during agent execution, and a single\n * progress tick appends a multi-MB ExecutionDump payload. Sync I/O here\n * blocked the loop for 20+ seconds per run, freezing IPC, scrcpy and\n * every renderer round-trip. Always use `fs/promises`. See commit\n * 6a25e05c and `report-generator-async-contract.test.ts`.\n */\nimport { existsSync, mkdirSync, readdirSync } from 'node:fs';\nimport {\n appendFile as appendFileAsync,\n readFile as readFileAsync,\n rename as renameAsync,\n unlink as unlinkAsync,\n writeFile as writeFileAsync,\n} from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport {\n MIDSCENE_REPORT_QUIET,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser, logMsg, uuid } from '@midscene/shared/utils';\nimport {\n DATA_SCREENSHOT_MODE_ATTR,\n generateAgentReportComment,\n generateDumpScriptTag,\n generateImageScriptTag,\n generateTestRunReportScriptTag,\n getBaseUrlFixScript,\n} from './dump/html-utils';\nimport {\n imageRefFileExtension,\n normalizeStoredImageRef,\n} from './dump/image-reference';\nimport { compactReportDumps } from './dump/report-dump-compactor';\nimport { type ImageUrlRef, ReportImageStore } from './dump/screenshot-store';\nimport type { WorkflowReportSource } from './test-runner/engine/types';\nimport { buildAgentTestRunReportDump } from './test-runner/reporting/agent-report';\nimport type { RunReportInput } from './test-runner/reporting/types';\nimport {\n type ExecutionDump,\n ReportActionDump,\n type ReportAttributes,\n type ReportMeta,\n type ScreenshotMode,\n} from './types';\nimport { getReportTpl } from './utils';\n\nconst warnReport = getDebug('report-generator', { console: true });\nconst maxReportFilenameBytes = 255;\n\nexport interface IReportGenerator {\n /**\n * Write or update a single execution.\n * Each call appends a new dump script tag. The frontend deduplicates\n * executions with the same id/name, keeping only the last one.\n *\n * @param execution Current execution's full data\n * @param reportMeta Report-level metadata (groupName, sdkVersion, etc.)\n */\n onExecutionUpdate(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n attributes?: ReportAttributes,\n ): void;\n\n /**\n * @deprecated Use onExecutionUpdate instead. Kept for backward compatibility.\n */\n onDumpUpdate?(dump: ReportActionDump): void;\n\n /**\n * Wait for all queued write operations to complete.\n */\n flush(): Promise<void>;\n enableSourceSnapshots?(): void;\n createSourceSnapshot?(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource | undefined>;\n\n /**\n * Finalize the report. Calls flush() internally.\n */\n finalize(): Promise<string | undefined>;\n\n getReportPath(): string | undefined;\n /** Publish a single root Runner snapshot into this Agent's existing report. */\n writeRunnerReport?(\n input: RunReportInput,\n scopeId: string,\n meta: ReportMeta,\n ): Promise<string | undefined>;\n}\n\nexport const nullReportGenerator: IReportGenerator = {\n onExecutionUpdate: () => {},\n flush: async () => {},\n finalize: async () => undefined,\n getReportPath: () => undefined,\n writeRunnerReport: async () => undefined,\n};\n\nexport function assertReportGenerationOptions(opts: {\n generateReport?: boolean;\n persistExecutionDump?: boolean;\n}): void {\n if (opts.generateReport === false && opts.persistExecutionDump === true) {\n throw new Error(\n 'persistExecutionDump cannot be true when generateReport is false',\n );\n }\n}\n\nexport class ReportGenerator implements IReportGenerator {\n private reportPath: string;\n private screenshotMode: ScreenshotMode;\n private shouldPersistExecutionDump: boolean;\n private autoPrint: boolean;\n private firstWriteDone = false;\n private executionLogIndex = 0;\n private executionLogFileIndexByExecutionKey = new Map<string, number>();\n\n // Unique identifier for this report stream — used as data-group-id\n private readonly reportStreamId: string;\n\n // Tracks screenshots already written to disk (by id) to avoid duplicates\n private screenshotStore: ReportImageStore;\n private initialized = false;\n\n // Tracks the last execution + groupMeta for re-writing on finalize\n private lastExecution?: ExecutionDump;\n private lastReportMeta?: ReportMeta;\n private executionsByKey = new Map<string, ExecutionDump>();\n private executionCommentKeyByObject = new WeakMap<ExecutionDump, string>();\n private executionCommentKeyIndex = 0;\n private reportAttributes: Record<string, string> = {};\n private agentCommentWritten = false;\n\n // write queue for serial execution\n private writeQueue: Promise<void> = Promise.resolve();\n private destroyed = false;\n\n constructor(options: {\n reportPath: string;\n screenshotMode: ScreenshotMode;\n persistExecutionDump?: boolean;\n autoPrint?: boolean;\n reuseExistingReport?: boolean;\n }) {\n this.reportPath = options.reportPath;\n this.screenshotMode = options.screenshotMode;\n this.shouldPersistExecutionDump = options.persistExecutionDump ?? false;\n this.autoPrint = options.autoPrint ?? true;\n this.reportStreamId = uuid();\n this.screenshotStore = new ReportImageStore({\n mode: this.screenshotMode === 'inline' ? 'inline' : 'directory',\n reportPath: this.reportPath,\n screenshotsDir: join(dirname(this.reportPath), 'screenshots'),\n writeInlineImage: async (id, base64) => {\n await appendFileAsync(\n this.reportPath,\n `\\n${generateImageScriptTag(id, base64)}`,\n );\n },\n alsoWriteFileCopy: this.shouldPersistExecutionDump,\n reuseExistingReport: options.reuseExistingReport,\n });\n if (options.reuseExistingReport) {\n this.hydrateStateFromExistingReport();\n }\n }\n\n static create(\n reportFileName: string,\n opts: {\n generateReport?: boolean;\n persistExecutionDump?: boolean;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n autoPrintReportMsg?: boolean;\n reuseExistingReport?: boolean;\n },\n ): IReportGenerator {\n assertReportGenerationOptions(opts);\n validateReportFileName(reportFileName, opts.outputFormat);\n if (opts.generateReport === false) return nullReportGenerator;\n\n // In browser environment, file system is not available\n if (ifInBrowser) return nullReportGenerator;\n\n const reportRootDir = getMidsceneRunSubDir('report');\n const outputDir = join(reportRootDir, reportFileName);\n const reportPath =\n opts.outputFormat === 'html-and-external-assets'\n ? join(outputDir, 'index.html')\n : join(reportRootDir, ensureHtmlFileName(reportFileName));\n return new ReportGenerator({\n reportPath,\n screenshotMode:\n opts.outputFormat === 'html-and-external-assets'\n ? 'directory'\n : 'inline',\n persistExecutionDump: opts.persistExecutionDump,\n autoPrint: opts.autoPrintReportMsg,\n reuseExistingReport: opts.reuseExistingReport,\n });\n }\n\n onExecutionUpdate(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n attributes?: ReportAttributes,\n ): void {\n this.lastExecution = execution;\n this.lastReportMeta = reportMeta;\n this.executionsByKey.set(this.getExecutionCommentKey(execution), execution);\n this.mergeReportAttributes(attributes);\n this.writeQueue = this.writeQueue.then(async () => {\n if (this.destroyed) return;\n await this.doWriteExecution(execution, reportMeta);\n });\n }\n\n async flush(): Promise<void> {\n await this.writeQueue;\n }\n\n async finalize(): Promise<string | undefined> {\n // Re-write the last execution to capture any final state changes\n if (this.lastExecution && this.lastReportMeta) {\n this.onExecutionUpdate(this.lastExecution, this.lastReportMeta);\n }\n await this.flush();\n this.destroyed = true;\n\n if (!this.initialized) {\n // No executions were ever written — no file exists\n return undefined;\n }\n\n await this.appendAgentReportComment();\n try {\n await compactReportDumps(this.reportPath);\n } catch (error) {\n warnReport(\n `Failed to compact report ${this.reportPath}; keeping the uncompressed report: ${String(error)}`,\n );\n }\n return this.reportPath;\n }\n\n getReportPath(): string | undefined {\n return this.reportPath;\n }\n\n async writeRunnerReport(\n input: RunReportInput,\n scopeId: string,\n meta: ReportMeta,\n ): Promise<string> {\n this.writeQueue = this.writeQueue.then(async () => {\n // This runs at YAML invocation completion, never on progress ticks. Keep\n // all file I/O asynchronous and publish atomically for an open viewer.\n const reportMeta = this.lastReportMeta ?? meta;\n const executions = [...this.executionsByKey.values()];\n const agentDump = new ReportActionDump({ ...reportMeta, executions });\n const reportId =\n this.reportAttributes['data-report-id'] ??\n this.reportAttributes['data-group-id'] ??\n this.reportStreamId;\n const dump = buildAgentTestRunReportDump(input, agentDump, {\n reportId,\n scopeId,\n sourcePath: this.reportPath,\n });\n for (const diagnostic of dump.diagnostics ?? [])\n warnReport(diagnostic.message);\n const sourceDump = await this.serializeSourceDump();\n const images: string[] = [];\n if (this.screenshotMode === 'inline') {\n for (const ref of sourceDump.images.values()) {\n const bytes = await readFileAsync(\n join(dirname(this.reportPath), ref.path!),\n );\n images.push(\n generateImageScriptTag(\n ref.id,\n `data:${ref.mimeType};base64,${bytes.toString('base64')}`,\n ),\n );\n }\n }\n // Rebuild from the owned dump and image index, never parse this HTML back.\n const renderedDump =\n this.screenshotMode === 'inline'\n ? JSON.stringify(JSON.parse(sourceDump.serialized), (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n return ref\n ? { ...ref, storage: 'inline', path: undefined }\n : value;\n })\n : sourceDump.serialized;\n const contents = `${getReportTpl()}${this.screenshotMode === 'directory' ? getBaseUrlFixScript() : ''}\n${generateDumpScriptTag(renderedDump, this.getDumpScriptAttributes())}\n${images.join('\\n')}\n${generateTestRunReportScriptTag(dump)}\n${generateAgentReportComment(agentDump)}`;\n mkdirSync(dirname(this.reportPath), { recursive: true });\n const temporaryPath = `${this.reportPath}.runner-${uuid()}.tmp`;\n try {\n await writeFileAsync(temporaryPath, contents);\n await renameAsync(temporaryPath, this.reportPath);\n } finally {\n await unlinkAsync(temporaryPath).catch((error) => {\n if (error.code !== 'ENOENT') throw error;\n });\n }\n this.initialized = true;\n if (!this.firstWriteDone) {\n this.firstWriteDone = true;\n this.printReportPath();\n }\n });\n await this.flush();\n return this.reportPath;\n }\n\n enableSourceSnapshots(): void {\n this.screenshotStore.enableFileCopies();\n }\n\n private async serializeSourceDump(\n frozenDump?: ReportActionDump,\n screenshots?: ReturnType<ExecutionDump['collectScreenshots']>,\n ) {\n this.enableSourceSnapshots();\n const executions = frozenDump?.executions ?? [\n ...this.executionsByKey.values(),\n ];\n const references = new Map<string, ImageUrlRef>();\n for (const screenshot of screenshots ??\n executions.flatMap((execution) => execution.collectScreenshots()))\n await this.screenshotStore.persist(screenshot);\n for (const execution of executions) {\n for (const [url, ref] of await this.persistReferenceImages(execution))\n references.set(url, ref);\n }\n const dump =\n frozenDump ??\n new ReportActionDump({\n ...(this.lastReportMeta ?? {\n sdkVersion: '',\n groupName: 'YAML execution',\n modelBriefs: [],\n }),\n executions,\n });\n const images = new Map<\n string,\n ReturnType<typeof normalizeStoredImageRef> & {}\n >();\n const serialized = JSON.stringify(\n JSON.parse(dump.serializeWithReferenceImages(references)),\n (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n if (!ref) return value;\n const stored = {\n ...ref,\n storage: 'file' as const,\n path: `./screenshots/${ref.id}.${imageRefFileExtension(ref)}`,\n };\n images.set(ref.id, stored);\n return stored;\n },\n );\n return { serialized, images };\n }\n\n async createSourceSnapshot(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource & { dumpPath: string }> {\n // Scope IDs originate in the kernel; never allow a filename to escape the report directory.\n if (!scopeId || basename(scopeId) !== scopeId)\n throw new Error('Invalid report scope ID');\n // Freeze before the first await. HTML writes and the live action can still\n // be pending; neither is a prerequisite for publishing this cutoff.\n const capturedAt = new Date().toISOString();\n const selectedIds = executionIds ? new Set(executionIds) : undefined;\n const executions = [...this.executionsByKey.values()].filter(\n (execution) =>\n !selectedIds || (execution.id && selectedIds.has(execution.id)),\n );\n const screenshots = executions.flatMap((execution) =>\n execution.collectScreenshots(),\n );\n const frozen = ReportActionDump.fromSerializedString(\n new ReportActionDump({\n ...(this.lastReportMeta ?? {\n sdkVersion: '',\n groupName: 'YAML execution',\n modelBriefs: [],\n }),\n executions,\n }).serialize(),\n );\n const { serialized } = await this.serializeSourceDump(frozen, screenshots);\n const dumpPath = `${this.reportPath}.${scopeId}.${uuid()}.json`;\n mkdirSync(dirname(dumpPath), { recursive: true });\n await writeFileAsync(dumpPath, serialized, { flag: 'wx' });\n return {\n sourcePath: this.reportPath,\n dumpPath,\n collection: { status: 'complete', capturedAt },\n };\n }\n\n private printReportPath(): void {\n if (!this.autoPrint || !this.reportPath) return;\n if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET))\n return;\n\n if (this.screenshotMode === 'directory') {\n logMsg(\n `Midscene - report file updated: npx serve ${dirname(this.reportPath)}`,\n );\n } else {\n logMsg(`Midscene - report file updated: ${this.reportPath}`);\n }\n }\n\n private async doWriteExecution(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n ): Promise<void> {\n const singleDump = this.wrapAsReportDump(execution, reportMeta);\n\n const referenceImageRefs = await this.writeExecution(execution, singleDump);\n\n if (this.shouldPersistExecutionDump) {\n await this.persistExecutionDumpToFile(\n execution,\n singleDump,\n referenceImageRefs,\n );\n }\n\n if (!this.firstWriteDone) {\n this.firstWriteDone = true;\n this.printReportPath();\n }\n }\n\n private mergeReportAttributes(attributes?: ReportAttributes): void {\n if (!attributes) {\n return;\n }\n\n for (const [key, value] of Object.entries(attributes)) {\n if (value === undefined || value === null) {\n continue;\n }\n this.reportAttributes[key] = String(value);\n }\n }\n\n private hydrateStateFromExistingReport(): void {\n if (!existsSync(this.reportPath)) {\n return;\n }\n\n // Reuse existing report file and append new updates instead of rewriting.\n this.initialized = true;\n\n if (!this.shouldPersistExecutionDump) {\n return;\n }\n\n const reportDir = dirname(this.reportPath);\n const existingExecutionIndices = readdirSync(reportDir)\n .map((name) => /^(\\d+)\\.execution\\.json$/.exec(name)?.[1])\n .filter((index): index is string => Boolean(index))\n .map((index) => Number.parseInt(index, 10))\n .filter((index) => Number.isFinite(index));\n\n if (existingExecutionIndices.length > 0) {\n this.executionLogIndex = Math.max(...existingExecutionIndices);\n }\n }\n\n private getDumpScriptAttributes(): Record<string, string> {\n return {\n 'data-group-id': this.reportStreamId,\n // Self-describe how this report file stores screenshots so consumers\n // (merge/delete) never have to guess the mode from the filesystem.\n [DATA_SCREENSHOT_MODE_ATTR]: this.screenshotMode,\n ...this.reportAttributes,\n };\n }\n\n /**\n * Wrap an ExecutionDump + ReportMeta into a single-execution ReportActionDump.\n */\n private wrapAsReportDump(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n ): ReportActionDump {\n return new ReportActionDump({\n sdkVersion: reportMeta.sdkVersion,\n groupName: reportMeta.groupName,\n groupDescription: reportMeta.groupDescription,\n modelBriefs: reportMeta.modelBriefs,\n deviceType: reportMeta.deviceType,\n executions: [execution],\n });\n }\n\n /**\n * Append assets and a dump tag without duplicating mode-specific workflows.\n * The frontend deduplicates executions with the same id/name (keeps last).\n * Duplicate dump JSON is acceptable; only screenshots are deduplicated.\n *\n * All writes go through `fs/promises` so they run on libuv's thread pool\n * rather than blocking the Node event loop. A long agent run previously\n * appended multi-MB dumps (screenshots + serialized tasks) per progress\n * tick on the main thread, starving IPC and UI for >10s per stall.\n */\n private async writeExecution(\n execution: ExecutionDump,\n singleDump: ReportActionDump,\n ): Promise<Map<string, ImageUrlRef>> {\n const dir = dirname(this.reportPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Initialize: write HTML template once\n if (!this.initialized) {\n const baseUrlFix =\n this.screenshotMode === 'directory' ? getBaseUrlFixScript() : '';\n await writeFileAsync(this.reportPath, `${getReportTpl()}${baseUrlFix}`);\n this.initialized = true;\n }\n\n // Append new screenshots (skip already-written ones)\n for (const screenshot of execution.collectScreenshots()) {\n await this.screenshotStore.persist(screenshot);\n }\n const referenceImageRefs = await this.persistReferenceImages(execution);\n\n // Append dump tag (always — frontend keeps only last per execution id)\n const serialized =\n singleDump.serializeWithReferenceImages(referenceImageRefs);\n await appendFileAsync(\n this.reportPath,\n `\\n${generateDumpScriptTag(serialized, this.getDumpScriptAttributes())}`,\n );\n return referenceImageRefs;\n }\n\n private async persistReferenceImages(\n execution: ExecutionDump,\n ): Promise<Map<string, ImageUrlRef>> {\n const refs = new Map<string, ImageUrlRef>();\n for (const imageUrl of execution.getReferenceImageUrls()) {\n const ref = await this.screenshotStore.persistReferenceImage(imageUrl);\n refs.set(imageUrl, ref);\n }\n return refs;\n }\n\n private async appendAgentReportComment(): Promise<void> {\n if (\n this.agentCommentWritten ||\n !this.lastReportMeta ||\n this.executionsByKey.size === 0\n ) {\n return;\n }\n\n const reportDump = new ReportActionDump({\n sdkVersion: this.lastReportMeta.sdkVersion,\n groupName: this.lastReportMeta.groupName,\n groupDescription: this.lastReportMeta.groupDescription,\n modelBriefs: this.lastReportMeta.modelBriefs,\n deviceType: this.lastReportMeta.deviceType,\n executions: Array.from(this.executionsByKey.values()),\n });\n await appendFileAsync(\n this.reportPath,\n `\\n${generateAgentReportComment(reportDump)}`,\n );\n this.agentCommentWritten = true;\n }\n\n private getExecutionLogKey(execution: ExecutionDump): string {\n if (!execution.id) {\n throw new Error(\n 'ReportGenerator: execution.id is required for persisting execution dumps',\n );\n }\n return `id:${execution.id}`;\n }\n\n private getExecutionCommentKey(execution: ExecutionDump): string {\n if (execution.id) {\n return `id:${execution.id}`;\n }\n\n const existingKey = this.executionCommentKeyByObject.get(execution);\n if (existingKey) {\n return existingKey;\n }\n\n const key = `no-id:${this.executionCommentKeyIndex++}`;\n this.executionCommentKeyByObject.set(execution, key);\n return key;\n }\n\n private async persistExecutionDumpToFile(\n execution: ExecutionDump,\n singleDump: ReportActionDump,\n referenceImageRefs: ReadonlyMap<string, ImageUrlRef>,\n ): Promise<void> {\n const dir = dirname(this.reportPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const executionLogKey = this.getExecutionLogKey(execution);\n let fileIndex =\n this.executionLogFileIndexByExecutionKey.get(executionLogKey);\n if (!fileIndex) {\n this.executionLogIndex += 1;\n fileIndex = this.executionLogIndex;\n this.executionLogFileIndexByExecutionKey.set(executionLogKey, fileIndex);\n }\n\n const fileName = `${fileIndex}.execution.json`;\n const filePath = join(dirname(this.reportPath), fileName);\n await writeFileAsync(\n filePath,\n singleDump.serializeWithReferenceImages(referenceImageRefs, 2),\n 'utf-8',\n );\n }\n}\n\nfunction ensureHtmlFileName(reportFileName: string): string {\n return reportFileName.endsWith('.html')\n ? reportFileName\n : `${reportFileName}.html`;\n}\n\nfunction validateReportFileName(\n reportFileName: string,\n outputFormat?: 'single-html' | 'html-and-external-assets',\n): void {\n if (!reportFileName?.trim()) {\n throw new Error('reportFileName must be a non-empty string');\n }\n\n if (/[\\\\/]/.test(reportFileName)) {\n throw new Error(\n 'reportFileName must not contain path separators (`/` or `\\\\\\\\`)',\n );\n }\n\n if (/[:*?\"<>|]/.test(reportFileName)) {\n throw new Error(\n 'reportFileName contains illegal filename characters: : * ? \" < > |',\n );\n }\n\n const filenameComponent =\n outputFormat === 'html-and-external-assets'\n ? reportFileName\n : ensureHtmlFileName(reportFileName);\n const filenameBytes = new TextEncoder().encode(filenameComponent).byteLength;\n if (filenameBytes > maxReportFilenameBytes) {\n throw new Error(\n `reportFileName produces a filename component of ${filenameBytes} UTF-8 bytes; maximum is ${maxReportFilenameBytes}`,\n );\n }\n}\n"],"names":["warnReport","getDebug","maxReportFilenameBytes","nullReportGenerator","undefined","assertReportGenerationOptions","opts","Error","ReportGenerator","reportFileName","validateReportFileName","ifInBrowser","reportRootDir","getMidsceneRunSubDir","outputDir","join","reportPath","ensureHtmlFileName","execution","reportMeta","attributes","compactReportDumps","error","String","input","scopeId","meta","executions","agentDump","ReportActionDump","reportId","dump","buildAgentTestRunReportDump","diagnostic","sourceDump","images","ref","bytes","readFileAsync","dirname","generateImageScriptTag","renderedDump","JSON","_key","value","normalizeStoredImageRef","contents","getReportTpl","getBaseUrlFixScript","generateDumpScriptTag","generateTestRunReportScriptTag","generateAgentReportComment","mkdirSync","temporaryPath","uuid","writeFileAsync","renameAsync","unlinkAsync","frozenDump","screenshots","references","Map","screenshot","url","serialized","stored","imageRefFileExtension","executionIds","basename","capturedAt","Date","selectedIds","Set","frozen","dumpPath","globalConfigManager","MIDSCENE_REPORT_QUIET","logMsg","singleDump","referenceImageRefs","key","Object","existsSync","reportDir","existingExecutionIndices","readdirSync","name","index","Boolean","Number","Math","DATA_SCREENSHOT_MODE_ATTR","dir","baseUrlFix","appendFileAsync","refs","imageUrl","reportDump","Array","existingKey","executionLogKey","fileIndex","fileName","filePath","options","WeakMap","Promise","ReportImageStore","id","base64","outputFormat","filenameComponent","filenameBytes","TextEncoder"],"mappings":"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"}
1
+ {"version":3,"file":"report-generator.mjs","sources":["../../src/report-generator.ts"],"sourcesContent":["/*\n * PERF INVARIANT — DO NOT reintroduce sync fs APIs (writeFileSync /\n * appendFileSync) in this file's write paths. `ReportGenerator` runs on\n * the Electron main event loop during agent execution, and a single\n * progress tick appends a multi-MB ExecutionDump payload. Sync I/O here\n * blocked the loop for 20+ seconds per run, freezing IPC, scrcpy and\n * every renderer round-trip. Always use `fs/promises`. See commit\n * 6a25e05c and `report-generator-async-contract.test.ts`.\n */\nimport { existsSync, mkdirSync, readdirSync } from 'node:fs';\nimport {\n appendFile as appendFileAsync,\n readFile as readFileAsync,\n rename as renameAsync,\n unlink as unlinkAsync,\n writeFile as writeFileAsync,\n} from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport {\n MIDSCENE_REPORT_QUIET,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug, logReport } from '@midscene/shared/logger';\nimport { ifInBrowser, uuid } from '@midscene/shared/utils';\nimport {\n DATA_SCREENSHOT_MODE_ATTR,\n generateAgentReportComment,\n generateDumpScriptTag,\n generateImageScriptTag,\n generateTestRunReportScriptTag,\n getBaseUrlFixScript,\n} from './dump/html-utils';\nimport {\n imageRefFileExtension,\n normalizeStoredImageRef,\n} from './dump/image-reference';\nimport { compactReportDumps } from './dump/report-dump-compactor';\nimport { type ImageUrlRef, ReportImageStore } from './dump/screenshot-store';\nimport type { WorkflowReportSource } from './test-runner/engine/types';\nimport { buildAgentTestRunReportDump } from './test-runner/reporting/agent-report';\nimport type { RunReportInput } from './test-runner/reporting/types';\nimport {\n type ExecutionDump,\n ReportActionDump,\n type ReportAttributes,\n type ReportMeta,\n type ScreenshotMode,\n} from './types';\nimport { getReportTpl } from './utils';\n\nconst warnReport = getDebug('report-generator', { console: true });\nconst maxReportFilenameBytes = 255;\n\nexport interface IReportGenerator {\n /**\n * Write or update a single execution.\n * Each call appends a new dump script tag. The frontend deduplicates\n * executions with the same id/name, keeping only the last one.\n *\n * @param execution Current execution's full data\n * @param reportMeta Report-level metadata (groupName, sdkVersion, etc.)\n */\n onExecutionUpdate(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n attributes?: ReportAttributes,\n ): void;\n\n /**\n * @deprecated Use onExecutionUpdate instead. Kept for backward compatibility.\n */\n onDumpUpdate?(dump: ReportActionDump): void;\n\n /**\n * Wait for all queued write operations to complete.\n */\n flush(): Promise<void>;\n enableSourceSnapshots?(): void;\n createSourceSnapshot?(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource | undefined>;\n\n /**\n * Finalize the report. Calls flush() internally.\n */\n finalize(): Promise<string | undefined>;\n\n getReportPath(): string | undefined;\n /** Publish a single root Runner snapshot into this Agent's existing report. */\n writeRunnerReport?(\n input: RunReportInput,\n scopeId: string,\n meta: ReportMeta,\n ): Promise<string | undefined>;\n}\n\nexport const nullReportGenerator: IReportGenerator = {\n onExecutionUpdate: () => {},\n flush: async () => {},\n finalize: async () => undefined,\n getReportPath: () => undefined,\n writeRunnerReport: async () => undefined,\n};\n\nexport function assertReportGenerationOptions(opts: {\n generateReport?: boolean;\n persistExecutionDump?: boolean;\n}): void {\n if (opts.generateReport === false && opts.persistExecutionDump === true) {\n throw new Error(\n 'persistExecutionDump cannot be true when generateReport is false',\n );\n }\n}\n\nexport class ReportGenerator implements IReportGenerator {\n private reportPath: string;\n private screenshotMode: ScreenshotMode;\n private shouldPersistExecutionDump: boolean;\n private autoPrint: boolean;\n private firstWriteDone = false;\n private executionLogIndex = 0;\n private executionLogFileIndexByExecutionKey = new Map<string, number>();\n\n // Unique identifier for this report stream — used as data-group-id\n private readonly reportStreamId: string;\n\n // Tracks screenshots already written to disk (by id) to avoid duplicates\n private screenshotStore: ReportImageStore;\n private initialized = false;\n\n // Tracks the last execution + groupMeta for re-writing on finalize\n private lastExecution?: ExecutionDump;\n private lastReportMeta?: ReportMeta;\n private executionsByKey = new Map<string, ExecutionDump>();\n private executionCommentKeyByObject = new WeakMap<ExecutionDump, string>();\n private executionCommentKeyIndex = 0;\n private reportAttributes: Record<string, string> = {};\n private agentCommentWritten = false;\n\n // write queue for serial execution\n private writeQueue: Promise<void> = Promise.resolve();\n private finalization?: Promise<string | undefined>;\n private sourcePublications = new Set<Promise<unknown>>();\n\n constructor(options: {\n reportPath: string;\n screenshotMode: ScreenshotMode;\n persistExecutionDump?: boolean;\n autoPrint?: boolean;\n reuseExistingReport?: boolean;\n }) {\n this.reportPath = options.reportPath;\n this.screenshotMode = options.screenshotMode;\n this.shouldPersistExecutionDump = options.persistExecutionDump ?? false;\n this.autoPrint = options.autoPrint ?? true;\n this.reportStreamId = uuid();\n this.screenshotStore = new ReportImageStore({\n mode: this.screenshotMode === 'inline' ? 'inline' : 'directory',\n reportPath: this.reportPath,\n screenshotsDir: join(dirname(this.reportPath), 'screenshots'),\n writeInlineImage: async (id, base64) => {\n await appendFileAsync(\n this.reportPath,\n `\\n${generateImageScriptTag(id, base64)}`,\n );\n },\n alsoWriteFileCopy: this.shouldPersistExecutionDump,\n reuseExistingReport: options.reuseExistingReport,\n });\n if (options.reuseExistingReport) {\n this.hydrateStateFromExistingReport();\n }\n }\n\n static create(\n reportFileName: string,\n opts: {\n generateReport?: boolean;\n persistExecutionDump?: boolean;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n autoPrintReportMsg?: boolean;\n reuseExistingReport?: boolean;\n },\n ): IReportGenerator {\n assertReportGenerationOptions(opts);\n validateReportFileName(reportFileName, opts.outputFormat);\n if (opts.generateReport === false) return nullReportGenerator;\n\n // In browser environment, file system is not available\n if (ifInBrowser) return nullReportGenerator;\n\n const reportRootDir = getMidsceneRunSubDir('report');\n const outputDir = join(reportRootDir, reportFileName);\n const reportPath =\n opts.outputFormat === 'html-and-external-assets'\n ? join(outputDir, 'index.html')\n : join(reportRootDir, ensureHtmlFileName(reportFileName));\n return new ReportGenerator({\n reportPath,\n screenshotMode:\n opts.outputFormat === 'html-and-external-assets'\n ? 'directory'\n : 'inline',\n persistExecutionDump: opts.persistExecutionDump,\n autoPrint: opts.autoPrintReportMsg,\n reuseExistingReport: opts.reuseExistingReport,\n });\n }\n\n onExecutionUpdate(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n attributes?: ReportAttributes,\n ): void {\n this.assertOpen();\n this.lastExecution = execution;\n this.lastReportMeta = reportMeta;\n this.executionsByKey.set(this.getExecutionCommentKey(execution), execution);\n this.mergeReportAttributes(attributes);\n this.writeQueue = this.writeQueue.then(async () => {\n await this.doWriteExecution(execution, reportMeta);\n });\n }\n\n async flush(): Promise<void> {\n await this.writeQueue;\n }\n\n async finalize(): Promise<string | undefined> {\n if (!this.finalization) {\n // Enqueue the final state before sealing the generator to new writes.\n if (this.lastExecution && this.lastReportMeta) {\n this.onExecutionUpdate(this.lastExecution, this.lastReportMeta);\n }\n this.finalization = this.finishPublication();\n }\n return this.finalization;\n }\n\n private async finishPublication(): Promise<string | undefined> {\n // Snapshots freeze independently of the HTML queue, but both must be on\n // disk before their backing executions and diagnostic data are released.\n await Promise.all([this.flush(), ...this.sourcePublications]);\n if (this.initialized) {\n await this.appendAgentReportComment();\n try {\n await compactReportDumps(this.reportPath);\n } catch (error) {\n warnReport(\n `Failed to compact report ${this.reportPath}; keeping the uncompressed report: ${String(error)}`,\n );\n }\n }\n this.executionsByKey.clear();\n this.lastExecution = undefined;\n this.lastReportMeta = undefined;\n this.executionCommentKeyByObject = new WeakMap();\n this.executionLogFileIndexByExecutionKey.clear();\n return this.initialized ? this.reportPath : undefined;\n }\n\n private assertOpen(): void {\n if (this.finalization) throw new Error('The report has been finalized.');\n }\n\n getReportPath(): string | undefined {\n return this.reportPath;\n }\n\n async writeRunnerReport(\n input: RunReportInput,\n scopeId: string,\n meta: ReportMeta,\n ): Promise<string> {\n this.assertOpen();\n this.writeQueue = this.writeQueue.then(async () => {\n // This runs at YAML invocation completion, never on progress ticks. Keep\n // all file I/O asynchronous and publish atomically for an open viewer.\n const reportMeta = this.lastReportMeta ?? meta;\n const executions = [...this.executionsByKey.values()];\n const agentDump = new ReportActionDump({ ...reportMeta, executions });\n const reportId =\n this.reportAttributes['data-report-id'] ??\n this.reportAttributes['data-group-id'] ??\n this.reportStreamId;\n const dump = buildAgentTestRunReportDump(input, agentDump, {\n reportId,\n scopeId,\n sourcePath: this.reportPath,\n });\n for (const diagnostic of dump.diagnostics ?? [])\n warnReport(diagnostic.message);\n const sourceDump = await this.serializeSourceDump();\n const images: string[] = [];\n if (this.screenshotMode === 'inline') {\n for (const ref of sourceDump.images.values()) {\n const bytes = await readFileAsync(\n join(dirname(this.reportPath), ref.path!),\n );\n images.push(\n generateImageScriptTag(\n ref.id,\n `data:${ref.mimeType};base64,${bytes.toString('base64')}`,\n ),\n );\n }\n }\n // Rebuild from the owned dump and image index, never parse this HTML back.\n const renderedDump =\n this.screenshotMode === 'inline'\n ? JSON.stringify(JSON.parse(sourceDump.serialized), (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n return ref\n ? { ...ref, storage: 'inline', path: undefined }\n : value;\n })\n : sourceDump.serialized;\n const contents = `${getReportTpl()}${this.screenshotMode === 'directory' ? getBaseUrlFixScript() : ''}\n${generateDumpScriptTag(renderedDump, this.getDumpScriptAttributes())}\n${images.join('\\n')}\n${generateTestRunReportScriptTag(dump)}\n${generateAgentReportComment(agentDump)}`;\n mkdirSync(dirname(this.reportPath), { recursive: true });\n const temporaryPath = `${this.reportPath}.runner-${uuid()}.tmp`;\n try {\n await writeFileAsync(temporaryPath, contents);\n await renameAsync(temporaryPath, this.reportPath);\n } finally {\n await unlinkAsync(temporaryPath).catch((error) => {\n if (error.code !== 'ENOENT') throw error;\n });\n }\n this.initialized = true;\n if (!this.firstWriteDone) {\n this.firstWriteDone = true;\n this.printReportPath();\n }\n });\n await this.flush();\n return this.reportPath;\n }\n\n enableSourceSnapshots(): void {\n this.screenshotStore.enableFileCopies();\n }\n\n private async serializeSourceDump(\n frozenDump?: ReportActionDump,\n screenshots?: ReturnType<ExecutionDump['collectScreenshots']>,\n ) {\n this.enableSourceSnapshots();\n const executions = frozenDump?.executions ?? [\n ...this.executionsByKey.values(),\n ];\n const references = new Map<string, ImageUrlRef>();\n for (const screenshot of screenshots ??\n executions.flatMap((execution) => execution.collectScreenshots()))\n await this.screenshotStore.persist(screenshot);\n for (const execution of executions) {\n for (const [url, ref] of await this.persistReferenceImages(execution))\n references.set(url, ref);\n }\n const dump =\n frozenDump ??\n new ReportActionDump({\n ...(this.lastReportMeta ?? {\n sdkVersion: '',\n groupName: 'YAML execution',\n modelBriefs: [],\n }),\n executions,\n });\n const images = new Map<\n string,\n ReturnType<typeof normalizeStoredImageRef> & {}\n >();\n const serialized = JSON.stringify(\n JSON.parse(dump.serializeWithReferenceImages(references)),\n (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n if (!ref) return value;\n const stored = {\n ...ref,\n storage: 'file' as const,\n path: `./screenshots/${ref.id}.${imageRefFileExtension(ref)}`,\n };\n images.set(ref.id, stored);\n return stored;\n },\n );\n return { serialized, images };\n }\n\n async createSourceSnapshot(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource & { dumpPath: string }> {\n this.assertOpen();\n // Scope IDs originate in the kernel; never allow a filename to escape the report directory.\n if (!scopeId || basename(scopeId) !== scopeId)\n throw new Error('Invalid report scope ID');\n // Freeze before the first await. HTML writes and the live action can still\n // be pending; neither is a prerequisite for publishing this cutoff.\n const capturedAt = new Date().toISOString();\n const selectedIds = executionIds ? new Set(executionIds) : undefined;\n const executions = [...this.executionsByKey.values()].filter(\n (execution) =>\n !selectedIds || (execution.id && selectedIds.has(execution.id)),\n );\n const screenshots = executions.flatMap((execution) =>\n execution.collectScreenshots(),\n );\n const frozen = ReportActionDump.fromSerializedString(\n new ReportActionDump({\n ...(this.lastReportMeta ?? {\n sdkVersion: '',\n groupName: 'YAML execution',\n modelBriefs: [],\n }),\n executions,\n }).serialize(),\n );\n const publication = (async () => {\n const { serialized } = await this.serializeSourceDump(\n frozen,\n screenshots,\n );\n const dumpPath = `${this.reportPath}.${scopeId}.${uuid()}.json`;\n mkdirSync(dirname(dumpPath), { recursive: true });\n await writeFileAsync(dumpPath, serialized, { flag: 'wx' });\n return {\n sourcePath: this.reportPath,\n dumpPath,\n collection: { status: 'complete' as const, capturedAt },\n };\n })();\n this.sourcePublications.add(publication);\n try {\n return await publication;\n } finally {\n this.sourcePublications.delete(publication);\n }\n }\n\n private printReportPath(): void {\n if (!this.autoPrint || !this.reportPath) return;\n if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET))\n return;\n\n if (this.screenshotMode === 'directory') {\n logReport(\n `Midscene - report file updated: npx serve ${dirname(this.reportPath)}`,\n );\n } else {\n logReport(`Midscene - report file updated: ${this.reportPath}`);\n }\n }\n\n private async doWriteExecution(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n ): Promise<void> {\n const singleDump = this.wrapAsReportDump(execution, reportMeta);\n\n const referenceImageRefs = await this.writeExecution(execution, singleDump);\n\n if (this.shouldPersistExecutionDump) {\n await this.persistExecutionDumpToFile(\n execution,\n singleDump,\n referenceImageRefs,\n );\n }\n\n if (!this.firstWriteDone) {\n this.firstWriteDone = true;\n this.printReportPath();\n }\n }\n\n private mergeReportAttributes(attributes?: ReportAttributes): void {\n if (!attributes) {\n return;\n }\n\n for (const [key, value] of Object.entries(attributes)) {\n if (value === undefined || value === null) {\n continue;\n }\n this.reportAttributes[key] = String(value);\n }\n }\n\n private hydrateStateFromExistingReport(): void {\n if (!existsSync(this.reportPath)) {\n return;\n }\n\n // Reuse existing report file and append new updates instead of rewriting.\n this.initialized = true;\n\n if (!this.shouldPersistExecutionDump) {\n return;\n }\n\n if (!existsSync(this.executionDumpDir)) return;\n const existingExecutionIndices = readdirSync(this.executionDumpDir)\n .map((name) => /^(\\d+)\\.execution\\.json$/.exec(name)?.[1])\n .filter((index): index is string => Boolean(index))\n .map((index) => Number.parseInt(index, 10))\n .filter((index) => Number.isFinite(index));\n\n if (existingExecutionIndices.length > 0) {\n this.executionLogIndex = Math.max(...existingExecutionIndices);\n }\n }\n\n private getDumpScriptAttributes(): Record<string, string> {\n return {\n 'data-group-id': this.reportStreamId,\n // Self-describe how this report file stores screenshots so consumers\n // (merge/delete) never have to guess the mode from the filesystem.\n [DATA_SCREENSHOT_MODE_ATTR]: this.screenshotMode,\n ...this.reportAttributes,\n };\n }\n\n /**\n * Wrap an ExecutionDump + ReportMeta into a single-execution ReportActionDump.\n */\n private wrapAsReportDump(\n execution: ExecutionDump,\n reportMeta: ReportMeta,\n ): ReportActionDump {\n return new ReportActionDump({\n sdkVersion: reportMeta.sdkVersion,\n groupName: reportMeta.groupName,\n groupDescription: reportMeta.groupDescription,\n modelBriefs: reportMeta.modelBriefs,\n deviceType: reportMeta.deviceType,\n executions: [execution],\n });\n }\n\n /**\n * Append assets and a dump tag without duplicating mode-specific workflows.\n * The frontend deduplicates executions with the same id/name (keeps last).\n * Duplicate dump JSON is acceptable; only screenshots are deduplicated.\n *\n * All writes go through `fs/promises` so they run on libuv's thread pool\n * rather than blocking the Node event loop. A long agent run previously\n * appended multi-MB dumps (screenshots + serialized tasks) per progress\n * tick on the main thread, starving IPC and UI for >10s per stall.\n */\n private async writeExecution(\n execution: ExecutionDump,\n singleDump: ReportActionDump,\n ): Promise<Map<string, ImageUrlRef>> {\n const dir = dirname(this.reportPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Initialize: write HTML template once\n if (!this.initialized) {\n const baseUrlFix =\n this.screenshotMode === 'directory' ? getBaseUrlFixScript() : '';\n await writeFileAsync(this.reportPath, `${getReportTpl()}${baseUrlFix}`);\n this.initialized = true;\n }\n\n // Append new screenshots (skip already-written ones)\n for (const screenshot of execution.collectScreenshots()) {\n await this.screenshotStore.persist(screenshot);\n }\n const referenceImageRefs = await this.persistReferenceImages(execution);\n\n // Append dump tag (always — frontend keeps only last per execution id)\n const serialized =\n singleDump.serializeWithReferenceImages(referenceImageRefs);\n await appendFileAsync(\n this.reportPath,\n `\\n${generateDumpScriptTag(serialized, this.getDumpScriptAttributes())}`,\n );\n return referenceImageRefs;\n }\n\n private async persistReferenceImages(\n execution: ExecutionDump,\n ): Promise<Map<string, ImageUrlRef>> {\n const refs = new Map<string, ImageUrlRef>();\n for (const imageUrl of execution.getReferenceImageUrls()) {\n const ref = await this.screenshotStore.persistReferenceImage(imageUrl);\n refs.set(imageUrl, ref);\n }\n return refs;\n }\n\n private async appendAgentReportComment(): Promise<void> {\n if (\n this.agentCommentWritten ||\n !this.lastReportMeta ||\n this.executionsByKey.size === 0\n ) {\n return;\n }\n\n const reportDump = new ReportActionDump({\n sdkVersion: this.lastReportMeta.sdkVersion,\n groupName: this.lastReportMeta.groupName,\n groupDescription: this.lastReportMeta.groupDescription,\n modelBriefs: this.lastReportMeta.modelBriefs,\n deviceType: this.lastReportMeta.deviceType,\n executions: Array.from(this.executionsByKey.values()),\n });\n await appendFileAsync(\n this.reportPath,\n `\\n${generateAgentReportComment(reportDump)}`,\n );\n this.agentCommentWritten = true;\n }\n\n private getExecutionLogKey(execution: ExecutionDump): string {\n if (!execution.id) {\n throw new Error(\n 'ReportGenerator: execution.id is required for persisting execution dumps',\n );\n }\n return `id:${execution.id}`;\n }\n\n private getExecutionCommentKey(execution: ExecutionDump): string {\n if (execution.id) {\n return `id:${execution.id}`;\n }\n\n const existingKey = this.executionCommentKeyByObject.get(execution);\n if (existingKey) {\n return existingKey;\n }\n\n const key = `no-id:${this.executionCommentKeyIndex++}`;\n this.executionCommentKeyByObject.set(execution, key);\n return key;\n }\n\n private get executionDumpDir(): string {\n // Inline HTML files share their parent directory. Each needs its own\n // numbered JSON namespace; external-asset reports already have one.\n return this.screenshotMode === 'inline'\n ? join(\n dirname(this.reportPath),\n '.execution-dumps',\n basename(this.reportPath),\n )\n : dirname(this.reportPath);\n }\n\n private async persistExecutionDumpToFile(\n execution: ExecutionDump,\n singleDump: ReportActionDump,\n referenceImageRefs: ReadonlyMap<string, ImageUrlRef>,\n ): Promise<void> {\n const dir = this.executionDumpDir;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const executionLogKey = this.getExecutionLogKey(execution);\n let fileIndex =\n this.executionLogFileIndexByExecutionKey.get(executionLogKey);\n if (!fileIndex) {\n this.executionLogIndex += 1;\n fileIndex = this.executionLogIndex;\n this.executionLogFileIndexByExecutionKey.set(executionLogKey, fileIndex);\n }\n\n const fileName = `${fileIndex}.execution.json`;\n const filePath = join(dir, fileName);\n let serialized = singleDump.serializeWithReferenceImages(\n referenceImageRefs,\n 2,\n );\n if (this.screenshotMode === 'inline') {\n // JSON is two directories below the HTML. Point to the existing file\n // copies so it can be consumed without resolving inline HTML assets.\n serialized = JSON.stringify(\n JSON.parse(serialized),\n (_key, value) => {\n const ref = normalizeStoredImageRef(value);\n return ref\n ? {\n ...ref,\n storage: 'file',\n path: `../../screenshots/${ref.id}.${imageRefFileExtension(ref)}`,\n }\n : value;\n },\n 2,\n );\n }\n await writeFileAsync(filePath, serialized, 'utf-8');\n }\n}\n\nfunction ensureHtmlFileName(reportFileName: string): string {\n return reportFileName.endsWith('.html')\n ? reportFileName\n : `${reportFileName}.html`;\n}\n\nfunction validateReportFileName(\n reportFileName: string,\n outputFormat?: 'single-html' | 'html-and-external-assets',\n): void {\n if (!reportFileName?.trim()) {\n throw new Error('reportFileName must be a non-empty string');\n }\n\n if (/[\\\\/]/.test(reportFileName)) {\n throw new Error(\n 'reportFileName must not contain path separators (`/` or `\\\\\\\\`)',\n );\n }\n\n if (/[:*?\"<>|]/.test(reportFileName)) {\n throw new Error(\n 'reportFileName contains illegal filename characters: : * ? \" < > |',\n );\n }\n\n const filenameComponent =\n outputFormat === 'html-and-external-assets'\n ? reportFileName\n : ensureHtmlFileName(reportFileName);\n const filenameBytes = new TextEncoder().encode(filenameComponent).byteLength;\n if (filenameBytes > maxReportFilenameBytes) {\n throw new Error(\n `reportFileName produces a filename component of ${filenameBytes} UTF-8 bytes; maximum is ${maxReportFilenameBytes}`,\n );\n }\n}\n"],"names":["warnReport","getDebug","maxReportFilenameBytes","nullReportGenerator","undefined","assertReportGenerationOptions","opts","Error","ReportGenerator","reportFileName","validateReportFileName","ifInBrowser","reportRootDir","getMidsceneRunSubDir","outputDir","join","reportPath","ensureHtmlFileName","execution","reportMeta","attributes","Promise","compactReportDumps","error","String","WeakMap","input","scopeId","meta","executions","agentDump","ReportActionDump","reportId","dump","buildAgentTestRunReportDump","diagnostic","sourceDump","images","ref","bytes","readFileAsync","dirname","generateImageScriptTag","renderedDump","JSON","_key","value","normalizeStoredImageRef","contents","getReportTpl","getBaseUrlFixScript","generateDumpScriptTag","generateTestRunReportScriptTag","generateAgentReportComment","mkdirSync","temporaryPath","uuid","writeFileAsync","renameAsync","unlinkAsync","frozenDump","screenshots","references","Map","screenshot","url","serialized","stored","imageRefFileExtension","executionIds","basename","capturedAt","Date","selectedIds","Set","frozen","publication","dumpPath","globalConfigManager","MIDSCENE_REPORT_QUIET","logReport","singleDump","referenceImageRefs","key","Object","existsSync","existingExecutionIndices","readdirSync","name","index","Boolean","Number","Math","DATA_SCREENSHOT_MODE_ATTR","dir","baseUrlFix","appendFileAsync","refs","imageUrl","reportDump","Array","existingKey","executionLogKey","fileIndex","fileName","filePath","options","ReportImageStore","id","base64","outputFormat","filenameComponent","filenameBytes","TextEncoder"],"mappings":"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"}