testdriverai 4.0.73 → 4.0.75
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/index.js +13 -5
- package/lib/commands.js +12 -10
- package/lib/network.js +3 -0
- package/lib/redraw.js +101 -6
- package/lib/system.js +4 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -57,7 +57,8 @@ let executionHistory = [];
|
|
|
57
57
|
let errorCounts = {};
|
|
58
58
|
let errorLimit = 3;
|
|
59
59
|
let checkCount = 0;
|
|
60
|
-
let checkLimit =
|
|
60
|
+
let checkLimit = 3;
|
|
61
|
+
let lastScreenshot = null;
|
|
61
62
|
let rl;
|
|
62
63
|
|
|
63
64
|
// list of prompts that the user has given us
|
|
@@ -279,11 +280,17 @@ const check = async () => {
|
|
|
279
280
|
|
|
280
281
|
console.log("");
|
|
281
282
|
log.log("info", chalk.dim("checking..."), "testdriver");
|
|
282
|
-
|
|
283
|
+
|
|
284
|
+
let thisScreenshot =await system.captureScreenBase64();
|
|
285
|
+
let images = [lastScreenshot, thisScreenshot];
|
|
283
286
|
let mousePosition = await system.getMousePosition();
|
|
284
287
|
let activeWindow = await system.activeWin();
|
|
285
288
|
|
|
286
|
-
|
|
289
|
+
let response = await sdk.req("check", { tasks, images, mousePosition, activeWindow });
|
|
290
|
+
|
|
291
|
+
lastScreenshot = thisScreenshot;
|
|
292
|
+
|
|
293
|
+
return response;
|
|
287
294
|
};
|
|
288
295
|
|
|
289
296
|
// command is transformed from a single yml entry generated by the AI into a JSON object
|
|
@@ -458,7 +465,8 @@ const humanInput = async (currentTask, validateAndLoop = false) => {
|
|
|
458
465
|
|
|
459
466
|
log.log("info", "");
|
|
460
467
|
|
|
461
|
-
|
|
468
|
+
lastScreenshot = await system.captureScreenBase64();
|
|
469
|
+
|
|
462
470
|
const mdStream = log.createMarkdownStreamLogger();
|
|
463
471
|
let message = await sdk.req(
|
|
464
472
|
"input",
|
|
@@ -466,7 +474,7 @@ const humanInput = async (currentTask, validateAndLoop = false) => {
|
|
|
466
474
|
input: currentTask,
|
|
467
475
|
mousePosition: await system.getMousePosition(),
|
|
468
476
|
activeWindow: await system.activeWin(),
|
|
469
|
-
image,
|
|
477
|
+
image: lastScreenshot,
|
|
470
478
|
},
|
|
471
479
|
(chunk) => mdStream.log(chunk),
|
|
472
480
|
);
|
package/lib/commands.js
CHANGED
|
@@ -167,19 +167,19 @@ const scroll = async (direction = "down", amount = 300) => {
|
|
|
167
167
|
switch (direction) {
|
|
168
168
|
case "up":
|
|
169
169
|
await robot.keyTap("pageup");
|
|
170
|
-
await redraw.wait(
|
|
170
|
+
await redraw.wait(10000);
|
|
171
171
|
break;
|
|
172
172
|
case "down":
|
|
173
173
|
await robot.keyTap("pagedown");
|
|
174
|
-
await redraw.wait(
|
|
174
|
+
await redraw.wait(10000);
|
|
175
175
|
break;
|
|
176
176
|
case "left":
|
|
177
177
|
await robot.scrollMouse(amount * -1, 0);
|
|
178
|
-
await redraw.wait(
|
|
178
|
+
await redraw.wait(10000);
|
|
179
179
|
break;
|
|
180
180
|
case "right":
|
|
181
181
|
await robot.scrollMouse(amount, 0);
|
|
182
|
-
await redraw.wait(
|
|
182
|
+
await redraw.wait(10000);
|
|
183
183
|
break;
|
|
184
184
|
default:
|
|
185
185
|
throw new AiError("Direction not found");
|
|
@@ -199,7 +199,7 @@ const click = async (x, y, button = "left", click = "single") => {
|
|
|
199
199
|
robot.moveMouseSmooth(x, y, 0.1);
|
|
200
200
|
await delay(1000); // wait for the mouse to move
|
|
201
201
|
robot.mouseClick(button, double);
|
|
202
|
-
await redraw.wait(
|
|
202
|
+
await redraw.wait(10000);
|
|
203
203
|
return;
|
|
204
204
|
};
|
|
205
205
|
|
|
@@ -210,7 +210,7 @@ const hover = async (x, y) => {
|
|
|
210
210
|
y = parseInt(y);
|
|
211
211
|
|
|
212
212
|
await robot.moveMouseSmooth(x, y, 0.1);
|
|
213
|
-
await redraw.wait(
|
|
213
|
+
await redraw.wait(10000);
|
|
214
214
|
|
|
215
215
|
return;
|
|
216
216
|
};
|
|
@@ -236,6 +236,7 @@ let commands = {
|
|
|
236
236
|
|
|
237
237
|
log("info", "");
|
|
238
238
|
log("info", chalk.dim("thinking..."), true);
|
|
239
|
+
log("info", "");
|
|
239
240
|
|
|
240
241
|
const mdStream = logger.createMarkdownStreamLogger();
|
|
241
242
|
let response = await sdk.req(
|
|
@@ -270,6 +271,7 @@ let commands = {
|
|
|
270
271
|
// take a screenshot
|
|
271
272
|
log("info", "");
|
|
272
273
|
log("info", chalk.dim("thinking..."), true);
|
|
274
|
+
log("info", "");
|
|
273
275
|
|
|
274
276
|
let response = await sdk.req("hover/image", {
|
|
275
277
|
needle: description,
|
|
@@ -313,7 +315,7 @@ let commands = {
|
|
|
313
315
|
await redraw.start();
|
|
314
316
|
string = string.toString();
|
|
315
317
|
await robot.typeString(string);
|
|
316
|
-
await redraw.wait(
|
|
318
|
+
await redraw.wait(10000);
|
|
317
319
|
return;
|
|
318
320
|
},
|
|
319
321
|
// press keys
|
|
@@ -371,7 +373,7 @@ let commands = {
|
|
|
371
373
|
// finally, press the keys
|
|
372
374
|
robot.keyTap(keysPressed[0], modsToPress);
|
|
373
375
|
|
|
374
|
-
await redraw.wait(
|
|
376
|
+
await redraw.wait(10000);
|
|
375
377
|
|
|
376
378
|
// keyTap will release the normal keys, but will not release modifier keys
|
|
377
379
|
// so we need to release the modifier keys manually
|
|
@@ -457,7 +459,7 @@ let commands = {
|
|
|
457
459
|
),
|
|
458
460
|
true,
|
|
459
461
|
);
|
|
460
|
-
await redraw.wait(
|
|
462
|
+
await redraw.wait(10000);
|
|
461
463
|
}
|
|
462
464
|
}
|
|
463
465
|
|
|
@@ -486,7 +488,7 @@ let commands = {
|
|
|
486
488
|
await robot.keyTap("f", commandOrControl);
|
|
487
489
|
// type the text
|
|
488
490
|
await robot.typeString(text);
|
|
489
|
-
await redraw.wait(
|
|
491
|
+
await redraw.wait(10000);
|
|
490
492
|
await robot.keyTap("escape");
|
|
491
493
|
} catch (e) {
|
|
492
494
|
console.log(e);
|
package/lib/network.js
ADDED
package/lib/redraw.js
CHANGED
|
@@ -3,6 +3,80 @@ const os = require("os");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const { compare } = require("odiff-bin");
|
|
5
5
|
|
|
6
|
+
// network
|
|
7
|
+
const si = require('systeminformation');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
let lastTxBytes = null;
|
|
10
|
+
let lastRxBytes = null;
|
|
11
|
+
let measurements = [];
|
|
12
|
+
let networkSettled = true;
|
|
13
|
+
let lastUnsettled = null;
|
|
14
|
+
let watchNetwork = null;
|
|
15
|
+
let screenHasRedrawn = null;
|
|
16
|
+
|
|
17
|
+
async function resetState() {
|
|
18
|
+
lastTxBytes = null;
|
|
19
|
+
lastRxBytes = null;
|
|
20
|
+
measurements = [];
|
|
21
|
+
networkSettled = true;
|
|
22
|
+
lastUnsettled = null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function updateNetwork() {
|
|
26
|
+
si.networkStats().then(data => {
|
|
27
|
+
let thisRxBytes = data[0].rx_bytes;
|
|
28
|
+
let thisTxBytes = data[0].tx_bytes;
|
|
29
|
+
|
|
30
|
+
let diffRxBytes = lastRxBytes !== null ? thisRxBytes - lastRxBytes : 0;
|
|
31
|
+
let diffTxBytes = lastTxBytes !== null ? thisTxBytes - lastTxBytes : 0;
|
|
32
|
+
|
|
33
|
+
lastRxBytes = thisRxBytes;
|
|
34
|
+
lastTxBytes = thisTxBytes;
|
|
35
|
+
|
|
36
|
+
measurements.push({ rx: diffRxBytes, tx: diffTxBytes });
|
|
37
|
+
|
|
38
|
+
if (measurements.length > 60) {
|
|
39
|
+
measurements.shift();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let avgRx = measurements.reduce((acc, m) => acc + m.rx, 0) / measurements.length;
|
|
43
|
+
let avgTx = measurements.reduce((acc, m) => acc + m.tx, 0) / measurements.length;
|
|
44
|
+
|
|
45
|
+
let stdDevRx = Math.sqrt(measurements.reduce((acc, m) => acc + Math.pow(m.rx - avgRx, 2), 0) / measurements.length);
|
|
46
|
+
let stdDevTx = Math.sqrt(measurements.reduce((acc, m) => acc + Math.pow(m.tx - avgTx, 2), 0) / measurements.length);
|
|
47
|
+
|
|
48
|
+
let zIndexRx = stdDevRx !== 0 ? (diffRxBytes - avgRx) / stdDevRx : 0;
|
|
49
|
+
let zIndexTx = stdDevTx !== 0 ? (diffTxBytes - avgTx) / stdDevTx : 0;
|
|
50
|
+
|
|
51
|
+
// log time since unsettlement
|
|
52
|
+
|
|
53
|
+
if ((new Date().getTime() - lastUnsettled) < 2000) {
|
|
54
|
+
networkSettled = false;
|
|
55
|
+
} else {
|
|
56
|
+
|
|
57
|
+
if ((zIndexRx < 0 && zIndexTx < 0) ) {
|
|
58
|
+
lastUnsettled = null;
|
|
59
|
+
networkSettled = true;
|
|
60
|
+
} else {
|
|
61
|
+
lastUnsettled = new Date().getTime();
|
|
62
|
+
networkSettled = false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (process.env["DEV"]) {
|
|
68
|
+
|
|
69
|
+
if (!networkSettled) {
|
|
70
|
+
console.log(chalk.red(new Date().getTime(), `,${zIndexRx}`, `,${zIndexTx}`));
|
|
71
|
+
} else {
|
|
72
|
+
console.log(new Date().getTime(), `,${zIndexRx}`, `,${zIndexTx}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
6
80
|
async function imageIsDifferent(image1Url, image2Url) {
|
|
7
81
|
|
|
8
82
|
// generate a temporary file path
|
|
@@ -14,7 +88,7 @@ async function imageIsDifferent(image1Url, image2Url) {
|
|
|
14
88
|
tmpImage,
|
|
15
89
|
{
|
|
16
90
|
failOnLayoutDiff: false,
|
|
17
|
-
outputDiffMask: false
|
|
91
|
+
outputDiffMask: false
|
|
18
92
|
},
|
|
19
93
|
);
|
|
20
94
|
|
|
@@ -22,7 +96,7 @@ async function imageIsDifferent(image1Url, image2Url) {
|
|
|
22
96
|
return false;
|
|
23
97
|
} else {
|
|
24
98
|
if (reason === "pixel-diff") {
|
|
25
|
-
if (diffPercentage >
|
|
99
|
+
if (diffPercentage > 5) {
|
|
26
100
|
return true;
|
|
27
101
|
} else {
|
|
28
102
|
return false;
|
|
@@ -36,22 +110,43 @@ async function imageIsDifferent(image1Url, image2Url) {
|
|
|
36
110
|
let startImage = null;
|
|
37
111
|
|
|
38
112
|
async function start() {
|
|
113
|
+
resetState();
|
|
114
|
+
watchNetwork = setInterval(updateNetwork, 500);
|
|
39
115
|
startImage = await captureScreenPNG();
|
|
40
116
|
return startImage;
|
|
41
117
|
}
|
|
42
118
|
|
|
43
119
|
async function checkCondition(resolve, startTime, timeoutMs) {
|
|
120
|
+
|
|
44
121
|
let nowImage = await captureScreenPNG();
|
|
45
|
-
let
|
|
122
|
+
let timeElapsed = Date.now() - startTime;
|
|
123
|
+
|
|
124
|
+
if (!screenHasRedrawn) {
|
|
125
|
+
screenHasRedrawn = await imageIsDifferent(startImage, nowImage);
|
|
126
|
+
}
|
|
46
127
|
|
|
47
|
-
if (
|
|
128
|
+
if (screenHasRedrawn && networkSettled) {
|
|
129
|
+
clearInterval(watchNetwork);
|
|
48
130
|
resolve("Condition met");
|
|
49
|
-
} else if (Date.now() -
|
|
131
|
+
} else if (Date.now() - timeElapsed > timeoutMs) {
|
|
132
|
+
clearInterval(watchNetwork);
|
|
50
133
|
resolve("Timeout reached");
|
|
51
134
|
} else {
|
|
135
|
+
|
|
136
|
+
if (timeElapsed > 3000) {
|
|
137
|
+
|
|
138
|
+
if (!screenHasRedrawn) {
|
|
139
|
+
console.log(chalk.dim(` waiting for screen redraw...`));
|
|
140
|
+
}
|
|
141
|
+
if (!networkSettled) {
|
|
142
|
+
console.log(chalk.dim(` waiting for network to settle...`));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
52
147
|
setTimeout(() => {
|
|
53
148
|
checkCondition(resolve, startTime, timeoutMs);
|
|
54
|
-
},
|
|
149
|
+
}, 1000);
|
|
55
150
|
}
|
|
56
151
|
}
|
|
57
152
|
|
package/lib/system.js
CHANGED
|
@@ -37,6 +37,10 @@ const captureAndResize = async (scale = 1) => {
|
|
|
37
37
|
let step1 = tmpFilename();
|
|
38
38
|
let step2 = tmpFilename();
|
|
39
39
|
|
|
40
|
+
if (process.env["DEV"]) {
|
|
41
|
+
console.log(step2)
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
await screenshot({ filename: step1, format: "png" });
|
|
41
45
|
|
|
42
46
|
// resize to 1:1 px ratio
|