testdriverai 4.2.0-test.0 → 4.2.0-test.2
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/.github/dependabot.yml +11 -0
- package/.github/workflows/test_interp.yml +1 -0
- package/agent.js +27 -21
- package/electron/overlay.html +2 -2
- package/lib/commands.js +1 -4
- package/lib/generator.js +10 -4
- package/lib/sdk.js +1 -1
- package/lib/session.js +3 -1
- package/lib/system.js +23 -4
- package/package.json +9 -7
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "npm" # See documentation for possible values
|
|
9
|
+
directory: "/" # Location of package manifests
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
package/agent.js
CHANGED
|
@@ -202,8 +202,11 @@ if (!commandHistory.length) {
|
|
|
202
202
|
];
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
const exit = async (failed = true) => {
|
|
206
|
-
|
|
205
|
+
const exit = async (failed = true, shouldSave = false) => {
|
|
206
|
+
|
|
207
|
+
if (shouldSave) {
|
|
208
|
+
await save();
|
|
209
|
+
}
|
|
207
210
|
|
|
208
211
|
analytics.track("exit", { failed });
|
|
209
212
|
|
|
@@ -641,6 +644,16 @@ const actOnMarkdown = async (content, depth, pushToHistory = false) => {
|
|
|
641
644
|
// simple function to backfill the chat history with a prompt and
|
|
642
645
|
// then call `promptUser()` to get the user input
|
|
643
646
|
const firstPrompt = async () => {
|
|
647
|
+
|
|
648
|
+
// should be start of new session
|
|
649
|
+
const sessionRes = await sdk.req("session/start", {
|
|
650
|
+
systemInformationOsInfo: await system.getSystemInformationOsInfo(),
|
|
651
|
+
mousePosition: await system.getMousePosition(),
|
|
652
|
+
activeWindow: await system.activeWin(),
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
session.set(sessionRes.data.id);
|
|
656
|
+
|
|
644
657
|
// readline is what allows us to get user input
|
|
645
658
|
rl = readline.createInterface({
|
|
646
659
|
terminal: true,
|
|
@@ -681,7 +694,7 @@ const firstPrompt = async () => {
|
|
|
681
694
|
if (input.indexOf("/summarize") == 0) {
|
|
682
695
|
await summarize();
|
|
683
696
|
} else if (input.indexOf("/quit") == 0) {
|
|
684
|
-
await exit();
|
|
697
|
+
await exit(false, true);
|
|
685
698
|
} else if (input.indexOf("/save") == 0) {
|
|
686
699
|
await save({ filepath: commands[1] });
|
|
687
700
|
} else if (input.indexOf("/undo") == 0) {
|
|
@@ -691,7 +704,7 @@ const firstPrompt = async () => {
|
|
|
691
704
|
} else if (input.indexOf("/manual") == 0) {
|
|
692
705
|
await manualInput(commands.slice(1).join(" "));
|
|
693
706
|
} else if (input.indexOf("/run") == 0) {
|
|
694
|
-
await run(commands[1], commands[2], commands[3]);
|
|
707
|
+
await run(commands[1], commands[2] == "true", commands[3] == "true");
|
|
695
708
|
} else if (input.indexOf("/generate") == 0) {
|
|
696
709
|
await generate(commands[1], commands[2]);
|
|
697
710
|
} else {
|
|
@@ -705,7 +718,7 @@ const firstPrompt = async () => {
|
|
|
705
718
|
// if file exists, load it
|
|
706
719
|
if (fs.existsSync(thisFile)) {
|
|
707
720
|
analytics.track("load");
|
|
708
|
-
let object = await generator.
|
|
721
|
+
let object = await generator.hydrateFromYML(
|
|
709
722
|
fs.readFileSync(thisFile, "utf-8"),
|
|
710
723
|
);
|
|
711
724
|
|
|
@@ -835,7 +848,7 @@ let save = async ({ filepath = thisFile, silent = false } = {}) => {
|
|
|
835
848
|
}
|
|
836
849
|
|
|
837
850
|
// write reply to /tmp/oiResult.log.log
|
|
838
|
-
let regression = await generator.
|
|
851
|
+
let regression = await generator.dumpToYML(executionHistory);
|
|
839
852
|
try {
|
|
840
853
|
fs.writeFileSync(filepath, regression);
|
|
841
854
|
} catch (e) {
|
|
@@ -862,9 +875,10 @@ ${regression}
|
|
|
862
875
|
// this will load a regression test from a file location
|
|
863
876
|
// it parses the markdown file and executes the codeblocks exactly as if they were
|
|
864
877
|
// generated by the AI in a single prompt
|
|
865
|
-
let run = async (file,
|
|
878
|
+
let run = async (file, shouldSave = false, shouldExit = true) => {
|
|
866
879
|
|
|
867
880
|
setTerminalWindowTransparency(true);
|
|
881
|
+
emitter.emit(events.interactive, false);
|
|
868
882
|
|
|
869
883
|
log.log("info", chalk.cyan(`running ${file}...`));
|
|
870
884
|
|
|
@@ -884,10 +898,10 @@ let run = async (file, overwrite = false, shouldExit = true) => {
|
|
|
884
898
|
await exit(true);
|
|
885
899
|
}
|
|
886
900
|
|
|
887
|
-
|
|
888
|
-
|
|
901
|
+
let interpolationVars = JSON.parse(process.env["TD_INTERPOLATION_VARS"] || '{}');
|
|
902
|
+
|
|
889
903
|
// Inject environment variables into any ${VAR} strings
|
|
890
|
-
yml = parser.interpolate(yml,
|
|
904
|
+
yml = parser.interpolate(yml, interpolationVars);
|
|
891
905
|
|
|
892
906
|
console.log(yml)
|
|
893
907
|
let ymlObj = null;
|
|
@@ -937,13 +951,14 @@ ${yaml.dump(step)}
|
|
|
937
951
|
await actOnMarkdown(markdown, 0, true);
|
|
938
952
|
}
|
|
939
953
|
|
|
940
|
-
if (
|
|
954
|
+
if (shouldSave) {
|
|
941
955
|
await save({ filepath: file });
|
|
942
956
|
}
|
|
943
957
|
|
|
944
958
|
setTerminalWindowTransparency(false);
|
|
959
|
+
emitter.emit(events.interactive, true);
|
|
945
960
|
|
|
946
|
-
if (shouldExit
|
|
961
|
+
if (shouldExit) {
|
|
947
962
|
await summarize();
|
|
948
963
|
await exit(false);
|
|
949
964
|
}
|
|
@@ -1059,15 +1074,6 @@ const start = async () => {
|
|
|
1059
1074
|
console.log("");
|
|
1060
1075
|
}
|
|
1061
1076
|
|
|
1062
|
-
// should be start of new session
|
|
1063
|
-
const sessionRes = await sdk.req("session/start", {
|
|
1064
|
-
systemInformationOsInfo: await system.getSystemInformationOsInfo(),
|
|
1065
|
-
mousePosition: await system.getMousePosition(),
|
|
1066
|
-
activeWindow: await system.activeWin(),
|
|
1067
|
-
});
|
|
1068
|
-
|
|
1069
|
-
session.set(sessionRes.data);
|
|
1070
|
-
|
|
1071
1077
|
analytics.track("command", { command: thisCommand, file: thisFile });
|
|
1072
1078
|
|
|
1073
1079
|
if (thisCommand == "edit") {
|
package/electron/overlay.html
CHANGED
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
screenshotElement.classList.add('screenshot');
|
|
247
247
|
setTimeout(() => {
|
|
248
248
|
container.style.opacity = 1
|
|
249
|
-
},
|
|
249
|
+
}, 2000)
|
|
250
250
|
});
|
|
251
251
|
|
|
252
252
|
ipcRenderer.on(events.mouseClick,
|
|
@@ -305,4 +305,4 @@
|
|
|
305
305
|
</script>
|
|
306
306
|
</body>
|
|
307
307
|
|
|
308
|
-
</html>
|
|
308
|
+
</html>
|
package/lib/commands.js
CHANGED
|
@@ -160,15 +160,12 @@ const assert = async (assertion, shouldThrow = false, async = false) => {
|
|
|
160
160
|
return handleAssertResponse(response.data);
|
|
161
161
|
}
|
|
162
162
|
};
|
|
163
|
-
const scroll = async (direction = "down", amount = 300, method = "
|
|
163
|
+
const scroll = async (direction = "down", amount = 300, method = "mouse") => {
|
|
164
164
|
await redraw.start();
|
|
165
165
|
|
|
166
166
|
amount = parseInt(amount);
|
|
167
167
|
|
|
168
168
|
if (method === "mouse") {
|
|
169
|
-
// after experimenting, 200 is a good default for mouse, mostly as mouse will be called only when the user asks for it
|
|
170
|
-
// and that happens when keyboard scrolling cannot do things when there's a pop up that needs to be scrolled over and
|
|
171
|
-
// pop ups are usually smaller and needs a smaller amount of scrolling
|
|
172
169
|
amount = 200;
|
|
173
170
|
}
|
|
174
171
|
|
package/lib/generator.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const yaml = require("js-yaml");
|
|
3
3
|
const chalk = require("chalk");
|
|
4
4
|
const package = require("../package.json");
|
|
5
|
+
const session = require("./session");
|
|
5
6
|
// do the actual parsing
|
|
6
7
|
// this library is very strict
|
|
7
8
|
// note that errors are sent to the AI will it may self-heal
|
|
@@ -52,25 +53,30 @@ const jsonToManual = function (json, colors = true) {
|
|
|
52
53
|
return params;
|
|
53
54
|
};
|
|
54
55
|
|
|
55
|
-
const
|
|
56
|
+
const dumpToYML = async function (inputArray) {
|
|
57
|
+
|
|
56
58
|
// use yml dump to convert json to yml
|
|
57
59
|
let yml = await yaml.dump({
|
|
58
60
|
version: package.version,
|
|
61
|
+
session: session.get(),
|
|
59
62
|
steps: inputArray,
|
|
60
63
|
});
|
|
61
64
|
|
|
62
65
|
return yml;
|
|
63
66
|
};
|
|
64
67
|
|
|
65
|
-
const
|
|
68
|
+
const hydrateFromYML = async function (yml) {
|
|
66
69
|
// use yml load to convert yml to json
|
|
67
70
|
let json = await yaml.load(yml);
|
|
71
|
+
|
|
72
|
+
session.set(json.session);
|
|
73
|
+
|
|
68
74
|
return json;
|
|
69
75
|
};
|
|
70
76
|
|
|
71
77
|
module.exports = {
|
|
72
78
|
manualToYml,
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
dumpToYML,
|
|
80
|
+
hydrateFromYML,
|
|
75
81
|
jsonToManual,
|
|
76
82
|
};
|
package/lib/sdk.js
CHANGED
|
@@ -106,7 +106,7 @@ const req = async (path, data, onChunk) => {
|
|
|
106
106
|
responseType: typeof onChunk === "function" ? "stream" : "json",
|
|
107
107
|
data: {
|
|
108
108
|
...data,
|
|
109
|
-
session: session.get()
|
|
109
|
+
session: session.get(),
|
|
110
110
|
stream: typeof onChunk === "function",
|
|
111
111
|
},
|
|
112
112
|
};
|
package/lib/session.js
CHANGED
package/lib/system.js
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const os = require("os");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const screenshot = require("screenshot-desktop");
|
|
6
5
|
const si = require("systeminformation");
|
|
7
|
-
const activeWindow = require("active-win");
|
|
8
6
|
const robot = require("robotjs");
|
|
9
7
|
const sharp = require("sharp");
|
|
10
8
|
const { emitter, events } = require("./events.js");
|
|
9
|
+
const { Monitor } = require("node-screenshots");
|
|
11
10
|
|
|
12
11
|
let primaryDisplay = null;
|
|
13
12
|
|
|
@@ -48,7 +47,11 @@ const captureAndResize = async (scale = 1, silent = false, mouse = false) => {
|
|
|
48
47
|
let step1 = tmpFilename();
|
|
49
48
|
let step2 = tmpFilename();
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
const monitors = Monitor.all();
|
|
51
|
+
const primaryMonitor = monitors.find(monitor => monitor.isPrimary);
|
|
52
|
+
const image = await primaryMonitor.captureImage(); // Capture the image asynchronously
|
|
53
|
+
const buffer = await image.toPng(); // Convert the image to PNG format
|
|
54
|
+
fs.writeFileSync(step1, buffer); // Save the image to a file
|
|
52
55
|
|
|
53
56
|
// Fetch the mouse position
|
|
54
57
|
const mousePos = robot.getMousePos();
|
|
@@ -111,9 +114,25 @@ const platform = () => {
|
|
|
111
114
|
return platform;
|
|
112
115
|
};
|
|
113
116
|
|
|
117
|
+
// Import get-windows using dynamic import for ES module compatibility
|
|
118
|
+
let activeWindowFn = null;
|
|
119
|
+
const initializeActiveWindow = async () => {
|
|
120
|
+
if (!activeWindowFn) {
|
|
121
|
+
const { activeWindow } = await import('get-windows');
|
|
122
|
+
activeWindowFn = activeWindow;
|
|
123
|
+
}
|
|
124
|
+
return activeWindowFn;
|
|
125
|
+
};
|
|
126
|
+
|
|
114
127
|
// this is the focused window
|
|
115
128
|
const activeWin = async () => {
|
|
116
|
-
|
|
129
|
+
try {
|
|
130
|
+
const activeWindow = await initializeActiveWindow();
|
|
131
|
+
return await activeWindow();
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error('Error getting active window:', error);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
117
136
|
};
|
|
118
137
|
|
|
119
138
|
const getMousePosition = async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testdriverai",
|
|
3
|
-
"version": "4.2.0-test.
|
|
3
|
+
"version": "4.2.0-test.2",
|
|
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,7 +17,6 @@
|
|
|
17
17
|
"license": "ISC",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@electerm/strip-ansi": "^1.0.0",
|
|
20
|
-
"active-win": "^8.2.1",
|
|
21
20
|
"axios": "^1.7.7",
|
|
22
21
|
"chalk": "^4.1.2",
|
|
23
22
|
"cli-progress": "^3.12.0",
|
|
@@ -25,6 +24,7 @@
|
|
|
25
24
|
"decompress": "^4.2.1",
|
|
26
25
|
"dotenv": "^16.4.5",
|
|
27
26
|
"electron": "^33.0.2",
|
|
27
|
+
"get-windows": "^9.2.0",
|
|
28
28
|
"jimp": "^0.22.12",
|
|
29
29
|
"js-yaml": "^4.1.0",
|
|
30
30
|
"mac-screen-capture-permissions": "^2.1.0",
|
|
@@ -34,14 +34,13 @@
|
|
|
34
34
|
"marky": "^1.2.5",
|
|
35
35
|
"node-ipc": "^12.0.0",
|
|
36
36
|
"node-notifier": "^10.0.1",
|
|
37
|
+
"node-screenshots": "0.2.1",
|
|
37
38
|
"odiff-bin": "^3.1.2",
|
|
38
39
|
"prompts": "^2.4.2",
|
|
39
40
|
"remark-parse": "^11.0.0",
|
|
40
|
-
"rimraf": "^5.0.5",
|
|
41
41
|
"robotjs": "^0.6.0",
|
|
42
42
|
"sanitize-filename": "^1.6.3",
|
|
43
43
|
"say": "^0.16.0",
|
|
44
|
-
"screenshot-desktop": "^1.15.0",
|
|
45
44
|
"semver": "^7.6.2",
|
|
46
45
|
"sharp": "^0.33.5",
|
|
47
46
|
"systeminformation": "^5.23.5",
|
|
@@ -49,16 +48,19 @@
|
|
|
49
48
|
"uuid": "^10.0.0",
|
|
50
49
|
"winston": "^3.13.0"
|
|
51
50
|
},
|
|
51
|
+
"overrides": {
|
|
52
|
+
"glob": "^11.0.1",
|
|
53
|
+
"rimraf": "^5.0.10"
|
|
54
|
+
},
|
|
52
55
|
"devDependencies": {
|
|
53
56
|
"@eslint/js": "^9.10.0",
|
|
57
|
+
"chai": "^5.1.2",
|
|
54
58
|
"esbuild": "0.20.2",
|
|
55
59
|
"esbuild-plugin-fileloc": "^0.0.6",
|
|
56
60
|
"eslint": "^9.10.0",
|
|
57
61
|
"globals": "^15.9.0",
|
|
58
|
-
"node-addon-api": "^8.0.0",
|
|
59
|
-
"node-gyp": "^10.1.0",
|
|
60
62
|
"mocha": "^10.8.2",
|
|
61
|
-
"
|
|
63
|
+
"node-addon-api": "^8.0.0",
|
|
62
64
|
"prettier": "3.3.3"
|
|
63
65
|
},
|
|
64
66
|
"optionalDependencies": {
|