testdriverai 4.0.33 → 4.0.35
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 +43 -9
- package/lib/commands.js +9 -12
- package/lib/sdk.js +6 -2
- package/lib/system.js +12 -35
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -100,14 +100,49 @@ console.log('')
|
|
|
100
100
|
// individual run ID for this session
|
|
101
101
|
let runID = new Date().getTime();
|
|
102
102
|
|
|
103
|
+
function fileCompleter(line) {
|
|
104
|
+
line = line.slice(5); // remove /run
|
|
105
|
+
const lastSepIndex = line.lastIndexOf(path.sep);
|
|
106
|
+
let dir;
|
|
107
|
+
let partial;
|
|
108
|
+
if (lastSepIndex === -1) {
|
|
109
|
+
dir = '.';
|
|
110
|
+
partial = line;
|
|
111
|
+
}
|
|
112
|
+
else{
|
|
113
|
+
dir = line.slice(0, lastSepIndex + 1);
|
|
114
|
+
partial = line.slice(lastSepIndex + 1);
|
|
115
|
+
}
|
|
116
|
+
try{
|
|
117
|
+
const dirPath = path.resolve(process.cwd(), dir);
|
|
118
|
+
|
|
119
|
+
let files = fs.readdirSync(dirPath);
|
|
120
|
+
files = files.map(file => {
|
|
121
|
+
const fullFilePath = path.join(dirPath, file);
|
|
122
|
+
const fileStats = fs.statSync(fullFilePath);
|
|
123
|
+
return file + (fileStats.isDirectory() ? path.sep : ''); // add path.sep for dir
|
|
124
|
+
});
|
|
125
|
+
const matches = files.filter(file => file.startsWith(partial));
|
|
126
|
+
|
|
127
|
+
return [matches.length ? matches : files, partial];
|
|
128
|
+
}
|
|
129
|
+
catch(err){
|
|
130
|
+
return [[], partial];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
103
134
|
function completer(line) {
|
|
104
135
|
let completions = '/summarize /save /run /quit /explore /assert /undo /manual'.split(' ')
|
|
105
|
-
|
|
136
|
+
if(line.startsWith('/run ')){
|
|
137
|
+
return fileCompleter(line);
|
|
138
|
+
}
|
|
139
|
+
else{
|
|
106
140
|
completions.concat(tasks)
|
|
107
141
|
|
|
108
142
|
var hits = completions.filter(function(c) { return c.indexOf(line) == 0 })
|
|
109
143
|
// show all completions if none found
|
|
110
|
-
|
|
144
|
+
return [hits.length ? hits : completions, line]
|
|
145
|
+
}
|
|
111
146
|
}
|
|
112
147
|
|
|
113
148
|
if (!fs.existsSync(commandHistoryFile)) {
|
|
@@ -170,7 +205,7 @@ const haveAIResolveError = async (error, markdown, depth = 0, undo = false) => {
|
|
|
170
205
|
|
|
171
206
|
let image;
|
|
172
207
|
if (error.attachScreenshot) {
|
|
173
|
-
image = await system.captureScreenBase64();
|
|
208
|
+
image = await system.captureScreenBase64(.5);
|
|
174
209
|
} else {
|
|
175
210
|
image = null;
|
|
176
211
|
}
|
|
@@ -196,7 +231,7 @@ const check = async () => {
|
|
|
196
231
|
|
|
197
232
|
console.log('')
|
|
198
233
|
log.log('info', chalk.dim('checking...'), 'testdriver')
|
|
199
|
-
let image = await system.captureScreenBase64();
|
|
234
|
+
let image = await system.captureScreenBase64(.5);
|
|
200
235
|
let mousePosition = await system.getMousePosition();
|
|
201
236
|
let activeWindow = await system.activeWin();
|
|
202
237
|
return await sdk.req('check', {tasks, image, mousePosition, activeWindow});
|
|
@@ -382,7 +417,7 @@ const humanInput = async (currentTask, validateAndLoop = false) => {
|
|
|
382
417
|
|
|
383
418
|
log.log('info', '');
|
|
384
419
|
|
|
385
|
-
let image = await system.captureScreenBase64();
|
|
420
|
+
let image = await system.captureScreenBase64(.5);
|
|
386
421
|
let message = await sdk.req('input', {
|
|
387
422
|
input: currentTask,
|
|
388
423
|
mousePosition: await system.getMousePosition(),
|
|
@@ -409,7 +444,7 @@ const generate = async (type) => {
|
|
|
409
444
|
|
|
410
445
|
log.log('info', '');
|
|
411
446
|
|
|
412
|
-
let image = await system.captureScreenBase64();
|
|
447
|
+
let image = await system.captureScreenBase64(.5);
|
|
413
448
|
let message = await sdk.req('generate', {
|
|
414
449
|
type,
|
|
415
450
|
image});
|
|
@@ -420,9 +455,8 @@ const generate = async (type) => {
|
|
|
420
455
|
|
|
421
456
|
// for each testPrompt
|
|
422
457
|
for (const testPrompt of testPrompts) {
|
|
423
|
-
// write a file called testprompt.headings[0].replace(' ', '-').toLowerCase().md
|
|
424
458
|
// with the contents of the testPrompt
|
|
425
|
-
let fileName = testPrompt.headings[0].trim().replace(/ /g, '-').toLowerCase() + '.md';
|
|
459
|
+
let fileName = sanitizeFilename(testPrompt.headings[0]).trim().replace(/ /g, '-').toLowerCase() + '.md';
|
|
426
460
|
let path1 = path.join(process.cwd(), 'testdriver', '.generate', fileName);
|
|
427
461
|
let contents = testPrompt.listsOrdered[0].map((item, index) => `${index + 1}. /explore ${item}`).join('\n');
|
|
428
462
|
fs.writeFileSync(path1, contents);
|
|
@@ -634,7 +668,7 @@ let summarize = async (error = null) => {
|
|
|
634
668
|
log.log('info', chalk.dim('reviewing test...'), true);
|
|
635
669
|
|
|
636
670
|
// let text = prompts.summarize(tasks, error);
|
|
637
|
-
let image = await system.captureScreenBase64();
|
|
671
|
+
let image = await system.captureScreenBase64(.5);
|
|
638
672
|
|
|
639
673
|
log.log('info', chalk.dim('summarizing...'), true);
|
|
640
674
|
|
package/lib/commands.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// the actual commands to interact with the system
|
|
2
2
|
const sdk = require('./sdk');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const {captureScreenBase64, captureScreenPNG, platform,
|
|
4
|
+
const {captureScreenBase64, captureScreenPNG, platform, activeWin} = require('./system');
|
|
5
5
|
const {log} = require('./logger');
|
|
6
6
|
const keymap = require('./keymap');
|
|
7
7
|
const { focusApplication } = require('./focus-application');
|
|
@@ -31,7 +31,6 @@ class AiError extends Error {
|
|
|
31
31
|
const commandOrControl = process.platform === 'darwin' ? 'command' : 'control';
|
|
32
32
|
|
|
33
33
|
const findImageOnScreen = async (relativePath, haystack, restrictToWindow) => {
|
|
34
|
-
let displayMultiple = await getDisplayMultiple();
|
|
35
34
|
|
|
36
35
|
// move the file from filePath to `testdriver/screenshots`
|
|
37
36
|
let rootpath = path.join(cwd(), `testdriver`, `screenshots`, platform());
|
|
@@ -94,10 +93,10 @@ const findImageOnScreen = async (relativePath, haystack, restrictToWindow) => {
|
|
|
94
93
|
|
|
95
94
|
results = results.filter((el) => {
|
|
96
95
|
|
|
97
|
-
return el.centerX
|
|
98
|
-
&& el.centerX
|
|
99
|
-
&& el.centerY
|
|
100
|
-
&& el.centerY
|
|
96
|
+
return el.centerX > activeWindow.bounds.x
|
|
97
|
+
&& el.centerX < activeWindow.bounds.x + activeWindow.bounds.width
|
|
98
|
+
&& el.centerY > activeWindow.bounds.y
|
|
99
|
+
&& el.centerY < activeWindow.bounds.y + activeWindow.bounds.height;
|
|
101
100
|
|
|
102
101
|
});
|
|
103
102
|
}
|
|
@@ -250,7 +249,7 @@ let commands = {
|
|
|
250
249
|
button,
|
|
251
250
|
clickType,
|
|
252
251
|
description,
|
|
253
|
-
displayMultiple:
|
|
252
|
+
displayMultiple: 1
|
|
254
253
|
});
|
|
255
254
|
|
|
256
255
|
if (!response.data) {
|
|
@@ -275,7 +274,7 @@ let commands = {
|
|
|
275
274
|
intent: action,
|
|
276
275
|
button,
|
|
277
276
|
clickType,
|
|
278
|
-
displayMultiple:
|
|
277
|
+
displayMultiple: 1
|
|
279
278
|
});
|
|
280
279
|
|
|
281
280
|
if (!response?.data) {
|
|
@@ -293,16 +292,14 @@ let commands = {
|
|
|
293
292
|
|
|
294
293
|
let result = await findImageOnScreen(relativePath, image)
|
|
295
294
|
|
|
296
|
-
let displayMultiple = await getDisplayMultiple();
|
|
297
|
-
|
|
298
295
|
if (!result) {
|
|
299
296
|
throw new AiError(`Image not found: ${relativePath}`)
|
|
300
297
|
} else {
|
|
301
298
|
|
|
302
299
|
if (action === 'click') {
|
|
303
|
-
await click(result.centerX
|
|
300
|
+
await click(result.centerX, result.centerY, button, clickType);
|
|
304
301
|
} else if (action === 'hover') {
|
|
305
|
-
await hover(result.centerX
|
|
302
|
+
await hover(result.centerX, result.centerY);
|
|
306
303
|
}
|
|
307
304
|
}
|
|
308
305
|
|
package/lib/sdk.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
// custom "sdk" for calling our API
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
let root = "https://replayable-api-production.herokuapp.com";
|
|
4
|
+
|
|
5
|
+
if (process.env['DEV']) {
|
|
6
|
+
root = "https://replayable-dev-ian-mac-m1-16.ngrok.io";
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
const chalk = require('chalk');
|
|
6
10
|
const axios = require('axios');
|
|
7
11
|
const session = require('./session')
|
package/lib/system.js
CHANGED
|
@@ -8,7 +8,6 @@ const activeWindow = require('active-win');
|
|
|
8
8
|
const robot = require('robotjs');
|
|
9
9
|
const sharp = require('sharp')
|
|
10
10
|
|
|
11
|
-
let displayMultiple = 0;
|
|
12
11
|
let primaryDisplay = null;
|
|
13
12
|
|
|
14
13
|
// get the primary display
|
|
@@ -27,54 +26,34 @@ const getSystemInformationOsInfo = async() => {
|
|
|
27
26
|
return await si.osInfo();
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
// this hepls us understand how to scale things for retina screens
|
|
31
|
-
const calculateDisplayMultiple = async () => {
|
|
32
|
-
let primaryDisplay = await getPrimaryDisplay();
|
|
33
|
-
displayMultiple = primaryDisplay.currentResX / primaryDisplay.resolutionX;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const getDisplayMultiple = async () => {
|
|
37
|
-
|
|
38
|
-
if (!displayMultiple) {
|
|
39
|
-
await calculateDisplayMultiple();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return displayMultiple;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
29
|
const tmpFilename = () => {
|
|
46
30
|
return path.join(os.tmpdir(), `${new Date().getTime() + Math.random()}.png`);
|
|
47
31
|
}
|
|
48
32
|
|
|
49
|
-
|
|
50
|
-
const captureScreenBase64 = async () => {
|
|
51
|
-
|
|
33
|
+
const captureAndResize = async(scale = 1) => {
|
|
52
34
|
let primaryDisplay = await getPrimaryDisplay();
|
|
53
35
|
|
|
54
36
|
let step1 = tmpFilename();
|
|
55
37
|
let step2 = tmpFilename();
|
|
56
38
|
|
|
57
|
-
await screenshot({ filename: step1, format: 'png' });
|
|
39
|
+
let step1Screenshot = await screenshot({ filename: step1, format: 'png' });
|
|
58
40
|
|
|
59
|
-
|
|
41
|
+
// resize to 1:1 px ratio
|
|
60
42
|
await sharp(step1)
|
|
61
|
-
.resize(primaryDisplay.currentResX, primaryDisplay.currentResY)
|
|
43
|
+
.resize(Math.floor(primaryDisplay.currentResX * scale), Math.floor(primaryDisplay.currentResY * scale))
|
|
62
44
|
.toFile(step2);
|
|
63
45
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return image;
|
|
46
|
+
return step2;
|
|
47
|
+
}
|
|
68
48
|
|
|
49
|
+
// our handy screenshot function
|
|
50
|
+
const captureScreenBase64 = async (scale = 1) => {
|
|
51
|
+
let step2 = await captureAndResize(scale);
|
|
52
|
+
return fs.readFileSync(step2, "base64");
|
|
69
53
|
};
|
|
70
54
|
|
|
71
|
-
const captureScreenPNG = async () => {
|
|
72
|
-
|
|
73
|
-
let step1 = tmpFilename();
|
|
74
|
-
await screenshot({ filename: step1, format: 'png' });
|
|
75
|
-
|
|
76
|
-
return step1;
|
|
77
|
-
|
|
55
|
+
const captureScreenPNG = async (scale = 1) => {
|
|
56
|
+
return await captureAndResize(scale);
|
|
78
57
|
}
|
|
79
58
|
|
|
80
59
|
const platform = () => {
|
|
@@ -103,8 +82,6 @@ const getMousePosition = async () => {
|
|
|
103
82
|
module.exports = {
|
|
104
83
|
captureScreenBase64,
|
|
105
84
|
captureScreenPNG,
|
|
106
|
-
getDisplayMultiple,
|
|
107
|
-
calculateDisplayMultiple,
|
|
108
85
|
getMousePosition,
|
|
109
86
|
primaryDisplay,
|
|
110
87
|
activeWin,
|