testdriverai 4.1.12 → 4.1.14
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.js +19 -15
- package/electron/overlay.js +4 -1
- package/index.js +43 -31
- package/lib/commander.js +6 -1
- package/lib/commands.js +5 -0
- package/lib/config.js +1 -1
- package/lib/focus-application.js +15 -10
- package/lib/logger.js +4 -3
- package/lib/overlay.js +1 -1
- package/lib/redraw.js +4 -13
- package/lib/sdk.js +55 -55
- package/lib/system.js +0 -4
- package/package.json +2 -1
package/agent.js
CHANGED
|
@@ -72,8 +72,6 @@ const args = process.argv.slice(2);
|
|
|
72
72
|
|
|
73
73
|
const commandHistoryFile = path.join(os.homedir(), ".testdriver_history");
|
|
74
74
|
|
|
75
|
-
const delay = (t) => new Promise((resolve) => setTimeout(resolve, t));
|
|
76
|
-
|
|
77
75
|
let getArgs = () => {
|
|
78
76
|
let command = 0;
|
|
79
77
|
let file = 1;
|
|
@@ -256,6 +254,7 @@ const haveAIResolveError = async (error, markdown, depth = 0, undo = false) => {
|
|
|
256
254
|
speak("thinking...");
|
|
257
255
|
notify("thinking...");
|
|
258
256
|
log.log("info", chalk.dim("thinking..."), true);
|
|
257
|
+
log.log("info", "");
|
|
259
258
|
|
|
260
259
|
let response = await sdk.req("error", {
|
|
261
260
|
description: eMessage,
|
|
@@ -280,9 +279,9 @@ const check = async () => {
|
|
|
280
279
|
return await exit(true);
|
|
281
280
|
}
|
|
282
281
|
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
log.log("info", "");
|
|
285
283
|
log.log("info", chalk.dim("checking..."), "testdriver");
|
|
284
|
+
log.log("info", "");
|
|
286
285
|
|
|
287
286
|
let thisScreenshot = await system.captureScreenBase64();
|
|
288
287
|
let images = [lastScreenshot, thisScreenshot];
|
|
@@ -452,6 +451,7 @@ const assert = async (expect) => {
|
|
|
452
451
|
speak("thinking...");
|
|
453
452
|
notify("thinking...");
|
|
454
453
|
log.log("info", chalk.dim("thinking..."), true);
|
|
454
|
+
log.log("info", "");
|
|
455
455
|
|
|
456
456
|
let response = `\`\`\`yml
|
|
457
457
|
commands:
|
|
@@ -477,7 +477,6 @@ const humanInput = async (currentTask, validateAndLoop = false) => {
|
|
|
477
477
|
speak("thinking...");
|
|
478
478
|
notify("thinking...");
|
|
479
479
|
log.log("info", chalk.dim("thinking..."), true);
|
|
480
|
-
|
|
481
480
|
log.log("info", "");
|
|
482
481
|
|
|
483
482
|
lastScreenshot = await system.captureScreenBase64();
|
|
@@ -492,6 +491,7 @@ const humanInput = async (currentTask, validateAndLoop = false) => {
|
|
|
492
491
|
image: lastScreenshot,
|
|
493
492
|
},
|
|
494
493
|
(chunk) => {
|
|
494
|
+
|
|
495
495
|
if (chunk.type === "data") {
|
|
496
496
|
mdStream.log(chunk.data);
|
|
497
497
|
}
|
|
@@ -513,7 +513,6 @@ const generate = async (type, count) => {
|
|
|
513
513
|
notify("thinking...");
|
|
514
514
|
|
|
515
515
|
log.log("info", chalk.dim("thinking..."), true);
|
|
516
|
-
|
|
517
516
|
log.log("info", "");
|
|
518
517
|
|
|
519
518
|
let image = await system.captureScreenBase64();
|
|
@@ -649,8 +648,7 @@ const firstPrompt = async () => {
|
|
|
649
648
|
if (!input.trim().length) return promptUser();
|
|
650
649
|
|
|
651
650
|
emitter.emit(events.interactive, false);
|
|
652
|
-
|
|
653
|
-
// setTerminalWindowTransparency(true);
|
|
651
|
+
setTerminalWindowTransparency(true);
|
|
654
652
|
errorCounts = {};
|
|
655
653
|
|
|
656
654
|
// append this to commandHistoryFile
|
|
@@ -683,7 +681,7 @@ const firstPrompt = async () => {
|
|
|
683
681
|
await humanInput(input, true);
|
|
684
682
|
}
|
|
685
683
|
|
|
686
|
-
|
|
684
|
+
setTerminalWindowTransparency(false);
|
|
687
685
|
promptUser();
|
|
688
686
|
});
|
|
689
687
|
|
|
@@ -726,6 +724,7 @@ New commands will be appended.
|
|
|
726
724
|
};
|
|
727
725
|
|
|
728
726
|
let setTerminalWindowTransparency = async (hide) => {
|
|
727
|
+
|
|
729
728
|
if (hide) {
|
|
730
729
|
try {
|
|
731
730
|
http
|
|
@@ -752,6 +751,7 @@ let setTerminalWindowTransparency = async (hide) => {
|
|
|
752
751
|
return;
|
|
753
752
|
}
|
|
754
753
|
|
|
754
|
+
|
|
755
755
|
try {
|
|
756
756
|
if (hide) {
|
|
757
757
|
if (terminalApp) {
|
|
@@ -775,7 +775,6 @@ let summarize = async (error = null) => {
|
|
|
775
775
|
|
|
776
776
|
log.log("info", "");
|
|
777
777
|
|
|
778
|
-
log.log("info", chalk.cyan("summarizing"));
|
|
779
778
|
log.log("info", chalk.dim("reviewing test..."), true);
|
|
780
779
|
|
|
781
780
|
// let text = prompts.summarize(tasks, error);
|
|
@@ -937,9 +936,9 @@ const promptUser = () => {
|
|
|
937
936
|
rl.prompt(true);
|
|
938
937
|
};
|
|
939
938
|
|
|
940
|
-
const setTerminalApp = async () => {
|
|
939
|
+
const setTerminalApp = async (win) => {
|
|
940
|
+
|
|
941
941
|
if (terminalApp) return;
|
|
942
|
-
let win = await system.activeWin();
|
|
943
942
|
if (process.platform === "win32") {
|
|
944
943
|
terminalApp = win?.title || "";
|
|
945
944
|
} else {
|
|
@@ -999,7 +998,8 @@ const embed = async (file, depth) => {
|
|
|
999
998
|
log.log("info", `${file} (end)`);
|
|
1000
999
|
};
|
|
1001
1000
|
|
|
1002
|
-
|
|
1001
|
+
const start = async () => {
|
|
1002
|
+
|
|
1003
1003
|
// console.log(await system.getPrimaryDisplay());
|
|
1004
1004
|
|
|
1005
1005
|
// @todo add-auth
|
|
@@ -1043,7 +1043,6 @@ const embed = async (file, depth) => {
|
|
|
1043
1043
|
console.log("");
|
|
1044
1044
|
}
|
|
1045
1045
|
|
|
1046
|
-
await setTerminalApp();
|
|
1047
1046
|
|
|
1048
1047
|
// should be start of new session
|
|
1049
1048
|
const sessionRes = await sdk.req("session/start", {
|
|
@@ -1064,7 +1063,7 @@ const embed = async (file, depth) => {
|
|
|
1064
1063
|
} else if (thisCommand == "init") {
|
|
1065
1064
|
init();
|
|
1066
1065
|
}
|
|
1067
|
-
}
|
|
1066
|
+
};
|
|
1068
1067
|
|
|
1069
1068
|
process.on("uncaughtException", async (err) => {
|
|
1070
1069
|
analytics.track("uncaughtException", { err });
|
|
@@ -1079,3 +1078,8 @@ process.on("unhandledRejection", async (reason, promise) => {
|
|
|
1079
1078
|
// Optionally, you might want to exit the process
|
|
1080
1079
|
await exit(true);
|
|
1081
1080
|
});
|
|
1081
|
+
|
|
1082
|
+
module.exports = {
|
|
1083
|
+
setTerminalApp,
|
|
1084
|
+
start
|
|
1085
|
+
};
|
package/electron/overlay.js
CHANGED
|
@@ -39,7 +39,10 @@ app.whenReady().then(() => {
|
|
|
39
39
|
visibleOnFullScreen: true,
|
|
40
40
|
});
|
|
41
41
|
window.loadFile("overlay.html");
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
window.once('ready-to-show', () => {
|
|
44
|
+
window.showInactive();
|
|
45
|
+
});
|
|
43
46
|
|
|
44
47
|
// open developer tools
|
|
45
48
|
// window.webContents.openDevTools();
|
package/index.js
CHANGED
|
@@ -1,37 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const config = require("./lib/config.js");
|
|
3
|
+
const system = require("./lib/system.js");
|
|
3
4
|
const { emitter, events } = require("./lib/events.js");
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
require("./agent.js");
|
|
7
|
-
} else {
|
|
8
|
-
// Intercept all stdout and stderr calls (works with console as well)
|
|
9
|
-
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
10
|
-
process.stdout.write = (...args) => {
|
|
11
|
-
const [data, encoding] = args;
|
|
12
|
-
emitter.emit(
|
|
13
|
-
events.terminal.stdout,
|
|
14
|
-
data.toString(typeof encoding === "string" ? encoding : undefined),
|
|
15
|
-
);
|
|
16
|
-
originalStdoutWrite(...args);
|
|
17
|
-
};
|
|
6
|
+
(async () => {
|
|
18
7
|
|
|
19
|
-
|
|
20
|
-
process.stderr.write = (...args) => {
|
|
21
|
-
const [data, encoding] = args;
|
|
22
|
-
emitter.emit(
|
|
23
|
-
events.terminal.stderr,
|
|
24
|
-
data.toString(typeof encoding === "string" ? encoding : undefined),
|
|
25
|
-
);
|
|
26
|
-
originalStderrWrite(...args);
|
|
27
|
-
};
|
|
8
|
+
let win = await system.activeWin();
|
|
28
9
|
|
|
29
|
-
|
|
30
|
-
.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
10
|
+
if (!config.TD_OVERLAY) {
|
|
11
|
+
let agent = require("./agent.js");
|
|
12
|
+
agent.setTerminalApp(win);
|
|
13
|
+
agent.start();
|
|
14
|
+
} else {
|
|
15
|
+
// Intercept all stdout and stderr calls (works with console as well)
|
|
16
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
17
|
+
process.stdout.write = (...args) => {
|
|
18
|
+
const [data, encoding] = args;
|
|
19
|
+
emitter.emit(
|
|
20
|
+
events.terminal.stdout,
|
|
21
|
+
data.toString(typeof encoding === "string" ? encoding : undefined),
|
|
22
|
+
);
|
|
23
|
+
originalStdoutWrite(...args);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
27
|
+
process.stderr.write = (...args) => {
|
|
28
|
+
const [data, encoding] = args;
|
|
29
|
+
emitter.emit(
|
|
30
|
+
events.terminal.stderr,
|
|
31
|
+
data.toString(typeof encoding === "string" ? encoding : undefined),
|
|
32
|
+
);
|
|
33
|
+
originalStderrWrite(...args);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
require("./lib/overlay.js")
|
|
37
|
+
.electronProcessPromise.then(() => {
|
|
38
|
+
let agent = require("./agent.js");
|
|
39
|
+
agent.setTerminalApp(win);
|
|
40
|
+
agent.start();
|
|
41
|
+
})
|
|
42
|
+
.catch((err) => {
|
|
43
|
+
console.error(err);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
})()
|
package/lib/commander.js
CHANGED
|
@@ -7,6 +7,7 @@ const speak = require("./speak");
|
|
|
7
7
|
const notify = require("./notify");
|
|
8
8
|
const analytics = require("./analytics");
|
|
9
9
|
const marky = require("marky");
|
|
10
|
+
const sdk = require("./sdk");
|
|
10
11
|
|
|
11
12
|
// object is a json representation of the individual yml command
|
|
12
13
|
// the process turns markdown -> yml -> json -> js function execution
|
|
@@ -181,7 +182,11 @@ commands:
|
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
let timing = marky.stop(object.command);
|
|
184
|
-
|
|
185
|
+
|
|
186
|
+
await Promise.all([
|
|
187
|
+
sdk.req('ran', { command: object.command, data: object }),
|
|
188
|
+
analytics.track("command", { data: object, depth, timing })
|
|
189
|
+
]);
|
|
185
190
|
|
|
186
191
|
return response;
|
|
187
192
|
};
|
package/lib/commands.js
CHANGED
|
@@ -139,6 +139,7 @@ const assert = async (assertion, shouldThrow = false, async = false) => {
|
|
|
139
139
|
// take a screenshot
|
|
140
140
|
log("info", "");
|
|
141
141
|
log("info", chalk.dim("thinking..."), true);
|
|
142
|
+
log("info", "");
|
|
142
143
|
|
|
143
144
|
if (async) {
|
|
144
145
|
await sdk
|
|
@@ -200,6 +201,7 @@ const click = async (x, y, button = "left", click = "single") => {
|
|
|
200
201
|
await delay(1000); // wait for the mouse to move
|
|
201
202
|
robot.mouseClick(button, double);
|
|
202
203
|
emitter.emit(events.mouseClick, { x, y, button, click });
|
|
204
|
+
|
|
203
205
|
await redraw.wait(5000);
|
|
204
206
|
return;
|
|
205
207
|
};
|
|
@@ -211,6 +213,7 @@ const hover = async (x, y) => {
|
|
|
211
213
|
y = parseInt(y);
|
|
212
214
|
|
|
213
215
|
await robot.moveMouseSmooth(x, y, 0.1);
|
|
216
|
+
|
|
214
217
|
await redraw.wait(2500);
|
|
215
218
|
|
|
216
219
|
return;
|
|
@@ -237,6 +240,7 @@ let commands = {
|
|
|
237
240
|
|
|
238
241
|
log("info", "");
|
|
239
242
|
log("info", chalk.dim("thinking..."), true);
|
|
243
|
+
log("info", "");
|
|
240
244
|
|
|
241
245
|
const mdStream = logger.createMarkdownStreamLogger();
|
|
242
246
|
let response = await sdk.req(
|
|
@@ -277,6 +281,7 @@ let commands = {
|
|
|
277
281
|
// take a screenshot
|
|
278
282
|
log("info", "");
|
|
279
283
|
log("info", chalk.dim("thinking..."), true);
|
|
284
|
+
log("info", "");
|
|
280
285
|
|
|
281
286
|
const mdStream = logger.createMarkdownStreamLogger();
|
|
282
287
|
let response = await sdk.req(
|
package/lib/config.js
CHANGED
package/lib/focus-application.js
CHANGED
|
@@ -3,20 +3,25 @@ const path = require("path");
|
|
|
3
3
|
const { execSync } = require("child_process");
|
|
4
4
|
const { platform } = require("./system");
|
|
5
5
|
const scriptPath = path.join(__dirname, "focusWindow.ps1");
|
|
6
|
+
const robot = require("robotjs");
|
|
6
7
|
|
|
7
8
|
// apple script that focuses on a window
|
|
8
|
-
const
|
|
9
|
+
const appleScriptSetFrontmost = (windowName) => `
|
|
9
10
|
tell application "System Events" to tell process "${windowName}"
|
|
10
11
|
set frontmost to true
|
|
11
12
|
end tell`;
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
-
tell application "System Events"
|
|
15
|
-
|
|
16
|
-
end tell`;
|
|
14
|
+
// const appleScriptAXMinimized = (windowName) => `
|
|
15
|
+
// tell application "System Events"
|
|
16
|
+
// set value of attribute "AXMinimized" of every window of application process "${windowName}" to true
|
|
17
|
+
// end tell`;
|
|
18
|
+
|
|
19
|
+
// const appleScriptActivate = (windowName) => `
|
|
20
|
+
// tell application "${windowName}" to activate
|
|
21
|
+
// `;
|
|
17
22
|
|
|
18
|
-
const
|
|
19
|
-
|
|
23
|
+
const appleScriptOpen = (windowName) => `
|
|
24
|
+
open -a "${windowName}"
|
|
20
25
|
`;
|
|
21
26
|
|
|
22
27
|
const runPwsh = (appName, method) => {
|
|
@@ -27,7 +32,7 @@ const runPwsh = (appName, method) => {
|
|
|
27
32
|
async function focusApplication(appName) {
|
|
28
33
|
try {
|
|
29
34
|
if (platform() == "mac") {
|
|
30
|
-
return await execSync(`osascript -e '${
|
|
35
|
+
return await execSync(`osascript -e '${appleScriptSetFrontmost(appName)}'`);
|
|
31
36
|
} else if (platform() == "linux") {
|
|
32
37
|
// TODO: This needs fixing
|
|
33
38
|
return;
|
|
@@ -42,7 +47,7 @@ async function focusApplication(appName) {
|
|
|
42
47
|
async function hideTerminal(appName) {
|
|
43
48
|
try {
|
|
44
49
|
if (platform() == "mac") {
|
|
45
|
-
|
|
50
|
+
robot.keyTap('m', ['command']);
|
|
46
51
|
} else if (platform() == "windows") {
|
|
47
52
|
return runPwsh(appName, "Minimize");
|
|
48
53
|
}
|
|
@@ -54,7 +59,7 @@ async function hideTerminal(appName) {
|
|
|
54
59
|
async function showTerminal(appName) {
|
|
55
60
|
try {
|
|
56
61
|
if (platform() == "mac") {
|
|
57
|
-
return await execSync(
|
|
62
|
+
return await execSync(appleScriptOpen(appName));
|
|
58
63
|
} else if (platform() == "windows") {
|
|
59
64
|
return runPwsh(appName, "Restore");
|
|
60
65
|
}
|
package/lib/logger.js
CHANGED
|
@@ -44,7 +44,7 @@ const markedParsePartial = (markdown, start = 0, end = 0) => {
|
|
|
44
44
|
|
|
45
45
|
const createMarkdownStreamLogger = () => {
|
|
46
46
|
let buffer = "";
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
return {
|
|
49
49
|
log: (chunk) => {
|
|
50
50
|
if (typeof chunk !== "string") {
|
|
@@ -65,10 +65,11 @@ const createMarkdownStreamLogger = () => {
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
end() {
|
|
68
|
-
const previousConsoleOutput = markedParsePartial(buffer, 0, -1);
|
|
69
68
|
|
|
70
|
-
const
|
|
69
|
+
const previousConsoleOutput = markedParsePartial(buffer, 0, -1);
|
|
70
|
+
const consoleOutput = markedParsePartial(buffer, 0, Infinity);
|
|
71
71
|
const diff = consoleOutput.replace(previousConsoleOutput, "");
|
|
72
|
+
|
|
72
73
|
if (diff) {
|
|
73
74
|
process.stdout.write(diff);
|
|
74
75
|
}
|
package/lib/overlay.js
CHANGED
package/lib/redraw.js
CHANGED
|
@@ -2,6 +2,7 @@ const { captureScreenPNG } = require("./system");
|
|
|
2
2
|
const os = require("os");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const { compare } = require("odiff-bin");
|
|
5
|
+
const logger = require("./logger");
|
|
5
6
|
|
|
6
7
|
// network
|
|
7
8
|
const si = require('systeminformation');
|
|
@@ -60,16 +61,6 @@ async function updateNetwork() {
|
|
|
60
61
|
} else {
|
|
61
62
|
networkSettled = false;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
-
if (process.env["DEV"]) {
|
|
65
|
-
|
|
66
|
-
if (!networkSettled) {
|
|
67
|
-
console.log(chalk.red(new Date().getTime(), `,${zIndexRx}`, `,${zIndexTx}`));
|
|
68
|
-
} else {
|
|
69
|
-
console.log(new Date().getTime(), `,${zIndexRx}`, `,${zIndexTx}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
}
|
|
73
64
|
|
|
74
65
|
});
|
|
75
66
|
}
|
|
@@ -125,10 +116,10 @@ async function checkCondition(resolve, startTime, timeoutMs) {
|
|
|
125
116
|
let networkText = networkSettled ? chalk.green(`y`) : chalk.dim(`${Math.trunc((diffRxBytes + diffTxBytes) / networkUpdateInterval)}b/s`);
|
|
126
117
|
let timeoutText = isTimeout ? chalk.green(`y`) : chalk.dim(`${Math.floor((timeElapsed)/1000)}/${(timeoutMs / 1000)}s`);
|
|
127
118
|
|
|
128
|
-
|
|
119
|
+
logger.log("debug", ` ` + chalk.dim('redraw=') + redrawText + chalk.dim(' network=') + networkText + chalk.dim(' timeout=') + timeoutText);
|
|
129
120
|
|
|
130
121
|
if ((screenHasRedrawn && networkSettled) || isTimeout) {
|
|
131
|
-
|
|
122
|
+
logger.log("debug", ` `);
|
|
132
123
|
resolve("true");
|
|
133
124
|
} else {
|
|
134
125
|
checkCondition(resolve, startTime, timeoutMs);
|
|
@@ -136,7 +127,7 @@ async function checkCondition(resolve, startTime, timeoutMs) {
|
|
|
136
127
|
}
|
|
137
128
|
|
|
138
129
|
function wait(timeoutMs) {
|
|
139
|
-
|
|
130
|
+
logger.log("debug", ` `);
|
|
140
131
|
return new Promise((resolve) => {
|
|
141
132
|
const startTime = Date.now();
|
|
142
133
|
checkCondition(resolve, startTime, timeoutMs);
|
package/lib/sdk.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const config = require("./config");
|
|
2
2
|
const chalk = require("chalk");
|
|
3
3
|
const session = require("./session");
|
|
4
|
-
const
|
|
5
|
-
const version = package.version;
|
|
4
|
+
const version = 'v4.1.0';
|
|
6
5
|
|
|
7
6
|
const root = config["TD_API_ROOT"];
|
|
7
|
+
const axios = require('axios');
|
|
8
|
+
|
|
9
|
+
const log = require('./logger');
|
|
8
10
|
|
|
9
11
|
// let token = null;
|
|
10
12
|
|
|
@@ -26,7 +28,7 @@ const parseBody = async (response, body) => {
|
|
|
26
28
|
if (!contentType.includes("json") && !contentType.includes("text")) {
|
|
27
29
|
return await response.arrayBuffer();
|
|
28
30
|
}
|
|
29
|
-
body =
|
|
31
|
+
body = response.data;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
if (typeof body === "string") {
|
|
@@ -83,7 +85,7 @@ let auth = async () => {
|
|
|
83
85
|
};
|
|
84
86
|
|
|
85
87
|
try {
|
|
86
|
-
await
|
|
88
|
+
await axios(url, config);
|
|
87
89
|
// token = res.data.token;
|
|
88
90
|
} catch (error) {
|
|
89
91
|
await outputError(error);
|
|
@@ -101,83 +103,81 @@ const req = async (path, data, onChunk) => {
|
|
|
101
103
|
|
|
102
104
|
const url = path.startsWith("/api")
|
|
103
105
|
? [root, path].join("")
|
|
104
|
-
: [root, "api",
|
|
106
|
+
: [root, "api", version, "testdriver", path].join("/");
|
|
107
|
+
|
|
108
|
+
log.log("debug", `making request to ${url}`);
|
|
105
109
|
|
|
106
110
|
const config = {
|
|
107
111
|
method: "post",
|
|
108
|
-
headers: { "Content-Type": "application/json" },
|
|
109
|
-
|
|
112
|
+
headers: { "Content-Type": "application/json" },
|
|
113
|
+
responseType: typeof onChunk === "function" ? "stream" : "json",
|
|
114
|
+
data: {
|
|
110
115
|
...data,
|
|
111
116
|
session: session.get()?.id,
|
|
112
117
|
stream: typeof onChunk === "function",
|
|
113
|
-
}
|
|
118
|
+
},
|
|
114
119
|
};
|
|
115
120
|
|
|
116
121
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (response.status >= 300) {
|
|
123
|
-
throw response;
|
|
124
|
-
}
|
|
125
|
-
const contentType = response.headers.get("Content-Type")?.toLowerCase();
|
|
122
|
+
let response;
|
|
123
|
+
|
|
124
|
+
response = await axios(url, config);
|
|
125
|
+
|
|
126
|
+
const contentType = response.headers["content-type"]?.toLowerCase();
|
|
126
127
|
const isJsonl = contentType === "application/jsonl";
|
|
127
128
|
let result;
|
|
129
|
+
|
|
128
130
|
if (onChunk) {
|
|
129
131
|
result = "";
|
|
130
132
|
let lastLineIndex = -1;
|
|
131
|
-
const reader = response.clone().body.getReader();
|
|
132
|
-
while (true) {
|
|
133
|
-
const { done, value } = await reader.read().catch((err) => {
|
|
134
|
-
console.error("Body read failed with error:", err);
|
|
135
|
-
return { done: true };
|
|
136
|
-
});
|
|
137
|
-
if (done) {
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
133
|
|
|
141
|
-
|
|
134
|
+
await new Promise((resolve, reject) => {
|
|
142
135
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
// theres some kind of race condition here that makes things resolve
|
|
137
|
+
// before the stream is done
|
|
138
|
+
|
|
139
|
+
response.data.on('data', (chunk) => {
|
|
140
|
+
|
|
141
|
+
result += chunk.toString();
|
|
146
142
|
const lines = result.split("\n");
|
|
147
|
-
|
|
143
|
+
|
|
144
|
+
const events = lines
|
|
148
145
|
.slice(lastLineIndex + 1, lines.length - 1)
|
|
149
146
|
.filter((line) => line.length)
|
|
150
|
-
.map((line) => JSON.parse(line));
|
|
147
|
+
.map((line) => JSON.parse(line));
|
|
148
|
+
|
|
149
|
+
for (const event of events) {
|
|
150
|
+
onChunk(event);
|
|
151
|
+
}
|
|
151
152
|
|
|
152
153
|
lastLineIndex = lines.length - 2;
|
|
153
|
-
}
|
|
154
|
-
for (const chunk of events) {
|
|
155
|
-
await onChunk(chunk);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
154
|
+
});
|
|
158
155
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
156
|
+
response.data.on('end', () => {
|
|
157
|
+
|
|
158
|
+
if (isJsonl) {
|
|
159
|
+
const events = result
|
|
160
|
+
.split("\n")
|
|
161
|
+
.slice(lastLineIndex + 2)
|
|
162
|
+
.filter((line) => line.length)
|
|
163
|
+
.map((line) => JSON.parse(line));
|
|
164
|
+
|
|
165
|
+
for (const event of events) {
|
|
166
|
+
onChunk(event);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
resolve();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
response.data.on('error', (error) => {
|
|
174
|
+
reject(error);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
169
177
|
}
|
|
170
178
|
|
|
171
179
|
const value = await parseBody(response, result);
|
|
172
180
|
|
|
173
|
-
if (!path.includes("analytics")) {
|
|
174
|
-
if (!value) {
|
|
175
|
-
throw new Error("Unexpected empty response body");
|
|
176
|
-
}
|
|
177
|
-
if (!("data" in value)) {
|
|
178
|
-
throw new Error("Missing data property in response body");
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
181
|
return value;
|
|
182
182
|
} catch (error) {
|
|
183
183
|
await outputError(error);
|
package/lib/system.js
CHANGED
|
@@ -46,10 +46,6 @@ const captureAndResize = async (scale = 1, silent = false) => {
|
|
|
46
46
|
let step1 = tmpFilename();
|
|
47
47
|
let step2 = tmpFilename();
|
|
48
48
|
|
|
49
|
-
if (process.env["DEV"]) {
|
|
50
|
-
console.log(step2);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
49
|
await screenshot({ filename: step1, format: "png" });
|
|
54
50
|
|
|
55
51
|
// Fetch the mouse position
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testdriverai",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.14",
|
|
4
4
|
"description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@electerm/strip-ansi": "^1.0.0",
|
|
19
19
|
"active-win": "^8.2.1",
|
|
20
|
+
"axios": "^1.7.7",
|
|
20
21
|
"chalk": "^4.1.2",
|
|
21
22
|
"cli-progress": "^3.12.0",
|
|
22
23
|
"datadog-winston": "^1.6.0",
|