@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
|
|
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: "#
|
|
87
|
+
haloFill: "#2f7df6",
|
|
88
88
|
haloOpacity: 0.28,
|
|
89
|
-
ringStroke: "#
|
|
89
|
+
ringStroke: "#2f7df6",
|
|
90
90
|
centerDotFill: "#07131f",
|
|
91
91
|
},
|
|
92
92
|
preview: {
|