@testdriverai/mcp 7.11.62-test → 7.11.63-test
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/agent/lib/redraw.js +5 -2
- package/agent/lib/system.js +33 -13
- package/package.json +1 -1
- package/vitest.unit.config.mjs +16 -2
package/agent/lib/redraw.js
CHANGED
|
@@ -232,7 +232,10 @@ const createRedraw = (
|
|
|
232
232
|
|
|
233
233
|
// Capture initial image for screen stability monitoring
|
|
234
234
|
if (currentOptions.screenRedraw) {
|
|
235
|
-
|
|
235
|
+
// remoteResize=true: these frames exist only to be pixel-diffed at
|
|
236
|
+
// quarter scale, so let the runner shrink them before upload rather
|
|
237
|
+
// than shipping full-resolution PNGs on every poll.
|
|
238
|
+
initialScreenImage = await system.captureScreenPNG(0.25, true, false, true);
|
|
236
239
|
lastScreenImage = initialScreenImage;
|
|
237
240
|
emitter.emit(events.log.debug, `[redraw] start() - captured initial image: ${initialScreenImage}`);
|
|
238
241
|
}
|
|
@@ -254,7 +257,7 @@ const createRedraw = (
|
|
|
254
257
|
await updateNetwork();
|
|
255
258
|
}
|
|
256
259
|
|
|
257
|
-
let nowImage = screenRedraw ? await system.captureScreenPNG(0.25, true) : null;
|
|
260
|
+
let nowImage = screenRedraw ? await system.captureScreenPNG(0.25, true, false, true) : null;
|
|
258
261
|
let timeElapsed = Date.now() - startTime;
|
|
259
262
|
let diffFromInitial = 0;
|
|
260
263
|
let diffFromLast = 0;
|
package/agent/lib/system.js
CHANGED
|
@@ -57,13 +57,21 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
57
57
|
// which is one of:
|
|
58
58
|
// { s3Key, width, height } — runner uploaded to S3 (Ably 64KB limit)
|
|
59
59
|
// { base64 } — direct/local connection, bytes inline
|
|
60
|
-
|
|
60
|
+
//
|
|
61
|
+
// `target` ({ width, height }) asks the runner to do the downscale before it
|
|
62
|
+
// uploads. Only pass it when the bytes are going to be shrunk to that size
|
|
63
|
+
// on arrival regardless — it saves a multi-megabyte round-trip per capture,
|
|
64
|
+
// but it also moves the resample from Jimp (here) to sharp (there), so the
|
|
65
|
+
// pixels differ slightly. Anything feeding vision or the selector cache must
|
|
66
|
+
// keep resizing locally.
|
|
67
|
+
const captureRaw = async (target = null) => {
|
|
61
68
|
return await sandbox.send({
|
|
62
69
|
type: "system.screenshot",
|
|
70
|
+
...(target ? { targetWidth: target.width, targetHeight: target.height } : {}),
|
|
63
71
|
});
|
|
64
72
|
};
|
|
65
73
|
|
|
66
|
-
const screenshot = async (options, rawResponse) => {
|
|
74
|
+
const screenshot = async (options, rawResponse, target = null) => {
|
|
67
75
|
const MAX_RETRIES = 3;
|
|
68
76
|
let lastError;
|
|
69
77
|
|
|
@@ -74,7 +82,7 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
74
82
|
// capture fresh.
|
|
75
83
|
let response = attempt === 0 && rawResponse
|
|
76
84
|
? rawResponse
|
|
77
|
-
: await captureRaw();
|
|
85
|
+
: await captureRaw(target);
|
|
78
86
|
|
|
79
87
|
let base64;
|
|
80
88
|
|
|
@@ -125,7 +133,11 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
125
133
|
return path.join(os.tmpdir(), `td-${Date.now()}-${randomUUID().slice(0, 8)}-${countImages}.png`);
|
|
126
134
|
};
|
|
127
135
|
|
|
128
|
-
|
|
136
|
+
// `remoteResize` pushes the downscale onto the runner so the frame never
|
|
137
|
+
// crosses the wire at full resolution. Off by default: it changes which
|
|
138
|
+
// resampler produced the pixels, which callers feeding vision or the
|
|
139
|
+
// selector cache must not do. See `captureRaw`.
|
|
140
|
+
const captureAndResize = async (scale = 1, silent = false, mouse = false, rawResponse = null, remoteResize = false) => {
|
|
129
141
|
try {
|
|
130
142
|
if (!silent) {
|
|
131
143
|
emitter.emit(events.screenCapture.start, {
|
|
@@ -138,21 +150,29 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
138
150
|
let step1 = tmpFilename();
|
|
139
151
|
let step2 = tmpFilename();
|
|
140
152
|
|
|
141
|
-
|
|
153
|
+
const targetWidth = Math.floor(config.TD_RESOLUTION[0] * scale);
|
|
154
|
+
const targetHeight = Math.floor(config.TD_RESOLUTION[1] * scale);
|
|
155
|
+
|
|
156
|
+
await screenshot(
|
|
157
|
+
{ filename: step1, format: "png" },
|
|
158
|
+
rawResponse,
|
|
159
|
+
remoteResize ? { width: targetWidth, height: targetHeight } : null,
|
|
160
|
+
);
|
|
142
161
|
|
|
143
162
|
// Load the screenshot image with Jimp
|
|
144
163
|
let image = await Jimp.read(step1);
|
|
145
|
-
|
|
164
|
+
|
|
146
165
|
// Validate the image was loaded correctly (not a 1x1 or tiny placeholder)
|
|
147
166
|
if (image.getWidth() < 10 || image.getHeight() < 10) {
|
|
148
167
|
throw new Error(`Screenshot appears corrupted: got ${image.getWidth()}x${image.getHeight()} pixels`);
|
|
149
168
|
}
|
|
150
169
|
|
|
151
|
-
// Resize the image
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
170
|
+
// Resize the image. Still unconditional when the runner already resized:
|
|
171
|
+
// an older runner ignores targetWidth/targetHeight and returns a
|
|
172
|
+
// full-size frame, and Jimp treats a resize to the current size as a
|
|
173
|
+
// no-op, so this both stays correct across versions and guarantees every
|
|
174
|
+
// frame comes out at the same dimensions for pixel diffing.
|
|
175
|
+
image.resize(targetWidth, targetHeight);
|
|
156
176
|
|
|
157
177
|
if (mouse) {
|
|
158
178
|
// Only get mouse position when needed to avoid unnecessary websocket calls
|
|
@@ -196,8 +216,8 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
196
216
|
return fs.readFileSync(step2, "base64");
|
|
197
217
|
};
|
|
198
218
|
|
|
199
|
-
const captureScreenPNG = async (scale = 1, silent = false, mouse = false) => {
|
|
200
|
-
return await captureAndResize(scale, silent, mouse);
|
|
219
|
+
const captureScreenPNG = async (scale = 1, silent = false, mouse = false, remoteResize = false) => {
|
|
220
|
+
return await captureAndResize(scale, silent, mouse, null, remoteResize);
|
|
201
221
|
};
|
|
202
222
|
|
|
203
223
|
// Build the image payload to send to the API for a command (find/assert/etc).
|
package/package.json
CHANGED
package/vitest.unit.config.mjs
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { defineConfig } from "vitest/config";
|
|
2
|
+
import { dirname, resolve } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const monoRoot = resolve(__dirname, "..");
|
|
7
|
+
|
|
8
|
+
// Unit tests for SDK and runner internals — pure, no sandbox, no network.
|
|
4
9
|
//
|
|
5
10
|
// Kept separate from vitest.config.mjs, which drives the real end-to-end
|
|
6
11
|
// examples: that config provisions live sandboxes, resolves per-channel API
|
|
7
12
|
// keys, and runs with an 8-minute timeout, so it can't host fast unit tests.
|
|
8
13
|
// Run with `npm run test:unit`.
|
|
14
|
+
//
|
|
15
|
+
// Rooted at the mono repo so runner/ tests can live here too (same pattern as
|
|
16
|
+
// vitest.runner.config.mjs) — dependencies still resolve from sdk/node_modules
|
|
17
|
+
// for SDK tests and runner/node_modules for runner tests.
|
|
9
18
|
export default defineConfig({
|
|
10
19
|
test: {
|
|
11
|
-
|
|
20
|
+
root: monoRoot,
|
|
21
|
+
include: [
|
|
22
|
+
"sdk/agent/**/*.test.mjs",
|
|
23
|
+
"sdk/lib/**/*.test.mjs",
|
|
24
|
+
"runner/lib/**/*.test.mjs",
|
|
25
|
+
],
|
|
12
26
|
// Don't let the e2e suites' fixtures/setup leak in.
|
|
13
27
|
setupFiles: [],
|
|
14
28
|
},
|