@loadmill/droid-cua 2.27.0 → 2.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,6 +101,7 @@ Desktop app support:
101
101
  - **Live execution logs** - Watch actions and progress as tests run
102
102
  - **Reports and history** - Review past runs from a project results folder inside the desktop app
103
103
  - **JUnit XML output** - Write standard test reports for CI systems and external tooling
104
+ - **Run video export** - Generate and download a polished MP4 replay from a desktop run's screenshots and instruction timeline
104
105
  - **Headless support** - Reuse scripts in CLI and automation workflows
105
106
 
106
107
  ---
@@ -290,6 +291,7 @@ HTML report (`--report`):
290
291
  - Writes a self-contained HTML report of the run — the same Run Details view the desktop app shows, with screenshots inlined — that you can share and open in any browser.
291
292
  - With no value it saves `<test>--<date>--<time>.html` (e.g. `login--2026-06-03--16-09-20.html`) in the current directory; pass `--report <file>` to pick a filename or `--report <dir>` to pick a folder.
292
293
  - In the desktop app the same report is available from a run's **Share → Download HTML report** button.
294
+ - Runs with captured screenshots also expose **Share → Generate video**. The desktop app renders a square mobile replay or 16:9 browser replay locally, then prompts for a save location using `<test>--<date>--<time>--test-replay.mp4` as the suggested filename.
293
295
  - Authenticated headless runs upload their HTML report and screenshots to Loadmill by default. Use `--no-cloud-report` to disable that artifact upload. The basic Droid run row is still reported to Loadmill.
294
296
 
295
297
  ```sh
@@ -399,7 +399,7 @@ export class ExecutionEngine {
399
399
  eventId: actionEventId,
400
400
  captureSource: CAPTURE_SOURCES.ACTION_CLICK_ANNOTATION
401
401
  });
402
- // Inline the green click marker as a data URL so self-contained reports show it.
402
+ // Inline the accent-colored click marker as a data URL so self-contained reports show it.
403
403
  // The Desktop recorder returns one from disk; for runs without a recorder (e.g. a
404
404
  // plain CLI `--report`) render the same marker in-memory from the raw screenshot.
405
405
  const annotatedDataUrl = await this.resolveAnnotatedDataUrl(annotatedScreenshot, currentScreenshot, action, context);
@@ -1,4 +1,29 @@
1
1
  import { buildTextToolOutputItem, parseStringToolArgument } from "./utils.js";
2
+ import { emitDesktopDebug } from "../../utils/desktop-debug.js";
3
+ function emitLoadmillToolCall({ callId, argumentsText, command, context, stepContext }) {
4
+ const scope = context?.sessionId ? "design" : "execution";
5
+ const ids = {
6
+ ...(scope === "design" ? { sessionId: context?.sessionId } : { runId: context?.runId }),
7
+ ...(stepContext?.stepId ? { stepId: stepContext.stepId } : {}),
8
+ ...(Number.isInteger(stepContext?.instructionIndex) ? { instructionIndex: stepContext.instructionIndex } : {}),
9
+ };
10
+ const payload = {
11
+ toolName: "loadmill",
12
+ callId,
13
+ command,
14
+ arguments: argumentsText,
15
+ };
16
+ emitDesktopDebug("tool_call", scope, ids, payload);
17
+ context?.addOutput?.({
18
+ type: "muted",
19
+ text: `Running Loadmill flow: ${command}`,
20
+ eventType: "tool_call",
21
+ runId: context?.runId,
22
+ stepId: stepContext?.stepId,
23
+ instructionIndex: stepContext?.instructionIndex,
24
+ payload,
25
+ });
26
+ }
2
27
  export async function handleLoadmillToolCall({ engine, callId, argumentsText, context = null, stepContext = null }) {
3
28
  const session = engine.session;
4
29
  const parsed = parseStringToolArgument(argumentsText, "command", "loadmill");
@@ -9,6 +34,13 @@ export async function handleLoadmillToolCall({ engine, callId, argumentsText, co
9
34
  };
10
35
  }
11
36
  const loadmillCommand = parsed.value;
37
+ emitLoadmillToolCall({
38
+ callId,
39
+ argumentsText,
40
+ command: loadmillCommand,
41
+ context,
42
+ stepContext,
43
+ });
12
44
  session.addToTranscript(`[Loadmill] ${loadmillCommand}`);
13
45
  const result = await engine.executeLoadmillInstruction(loadmillCommand, engine.isHeadlessMode, {
14
46
  ...context,
@@ -84,9 +84,9 @@ function buildMarkerPlacement(marker = {}, width, height) {
84
84
  }
85
85
  const MARKER_APPEARANCES = {
86
86
  click: {
87
- haloFill: "#39ff14",
87
+ haloFill: "#2f7df6",
88
88
  haloOpacity: 0.28,
89
- ringStroke: "#39ff14",
89
+ ringStroke: "#2f7df6",
90
90
  centerDotFill: "#07131f",
91
91
  },
92
92
  preview: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loadmill/droid-cua",
3
- "version": "2.27.0",
3
+ "version": "2.28.0",
4
4
  "description": "AI-powered Android testing agent using OpenAI's computer-use model and ADB",
5
5
  "main": "build/index.js",
6
6
  "type": "module",