testdriverai 4.2.20 → 5.0.0-beta.3
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 +1 -1
- package/.github/workflows/test_interp.yml +1 -1
- package/.github/workflows/testdriver.yml +28 -0
- package/README.md +3 -2
- package/agent.js +90 -24
- package/electron/overlay.html +38 -5
- package/electron/overlay.js +58 -25
- package/electron/td.png +0 -0
- package/index.js +2 -2
- package/lib/commander.js +10 -2
- package/lib/commands.js +94 -50
- package/lib/config.js +3 -1
- package/lib/events.js +4 -0
- package/lib/focus-application.js +16 -0
- package/lib/generator.js +16 -0
- package/lib/init.js +59 -21
- package/lib/keymaps/sandbox.js +124 -0
- package/lib/logger.js +4 -1
- package/lib/overlay.js +0 -2
- package/lib/redraw.js +3 -1
- package/lib/sandbox.js +52 -0
- package/lib/speak.js +3 -0
- package/lib/system.js +67 -23
- package/lib/websockets.js +78 -0
- package/package-lock.json +8705 -0
- package/package.json +8 -6
- package/test.md +8 -0
- /package/lib/{keymap.js → keymaps/robot.js} +0 -0
package/lib/speak.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
const say = require("say");
|
|
2
2
|
const config = require("./config");
|
|
3
|
+
const websockets = require("./websockets");
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
module.exports = (message) => {
|
|
5
7
|
if (config["TD_SPEAK"]) {
|
|
6
8
|
say.stop();
|
|
9
|
+
// websockets.sendToClients("output", message);
|
|
7
10
|
if (process.platform === "darwin") {
|
|
8
11
|
say.speak(message, "Fred", 1.2);
|
|
9
12
|
} else {
|
package/lib/system.js
CHANGED
|
@@ -2,13 +2,28 @@
|
|
|
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
6
|
const robot = require("robotjs");
|
|
8
7
|
const sharp = require("sharp");
|
|
9
8
|
const { emitter, events } = require("./events.js");
|
|
9
|
+
const sandbox = require("./sandbox.js");
|
|
10
|
+
const config = require("./config.js");
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
let scshotdesk;
|
|
13
|
+
if (!config.TD_VM) {
|
|
14
|
+
scshotdesk = require("screenshot-desktop");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const screenshot = async (options) => {
|
|
18
|
+
|
|
19
|
+
if (config.TD_VM) {
|
|
20
|
+
let {base64} = await sandbox.send({type: 'screenshot'});
|
|
21
|
+
let image = Buffer.from(base64, 'base64');
|
|
22
|
+
fs.writeFileSync(options.filename, image);
|
|
23
|
+
} else {
|
|
24
|
+
return await scshotdesk({ filename: options.filename, format: "png" });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
12
27
|
|
|
13
28
|
let primaryDisplay = null;
|
|
14
29
|
|
|
@@ -26,7 +41,13 @@ const getPrimaryDisplay = async () => {
|
|
|
26
41
|
};
|
|
27
42
|
|
|
28
43
|
const getSystemInformationOsInfo = async () => {
|
|
29
|
-
|
|
44
|
+
if (config.TD_VM) {
|
|
45
|
+
return {
|
|
46
|
+
os: 'linux'
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
return await si.osInfo();
|
|
50
|
+
}
|
|
30
51
|
};
|
|
31
52
|
|
|
32
53
|
let countImages = 0;
|
|
@@ -37,7 +58,7 @@ const tmpFilename = () => {
|
|
|
37
58
|
|
|
38
59
|
const captureAndResize = async (scale = 1, silent = false, mouse = false) => {
|
|
39
60
|
try {
|
|
40
|
-
|
|
61
|
+
|
|
41
62
|
if (!silent) {
|
|
42
63
|
emitter.emit(events.screenCapture.start, {
|
|
43
64
|
scale,
|
|
@@ -51,21 +72,35 @@ const captureAndResize = async (scale = 1, silent = false, mouse = false) => {
|
|
|
51
72
|
|
|
52
73
|
await screenshot({ filename: step1, format: "png" });
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
75
|
+
let sharpInstance;
|
|
76
|
+
if (config.TD_VM) {
|
|
77
|
+
|
|
78
|
+
// resize to 1:1 px ratio
|
|
79
|
+
sharpInstance = sharp(step1).resize(
|
|
80
|
+
Math.floor(1024 * scale),
|
|
81
|
+
Math.floor(768 * scale),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
} else {
|
|
56
85
|
|
|
57
|
-
|
|
58
|
-
|
|
86
|
+
const primaryDisplay = await getPrimaryDisplay();
|
|
87
|
+
|
|
88
|
+
sharpInstance = sharp(step1).resize(
|
|
89
|
+
Math.floor(primaryDisplay.currentResX * scale),
|
|
90
|
+
Math.floor(primaryDisplay.currentResY * scale),
|
|
91
|
+
);
|
|
59
92
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
93
|
+
// Fetch the mouse position
|
|
94
|
+
const mousePos = robot.getMousePos();
|
|
95
|
+
|
|
96
|
+
// Location of cursor image
|
|
97
|
+
const cursorPath = path.join(__dirname, "resources", "cursor.png");
|
|
98
|
+
|
|
99
|
+
if (mouse) {
|
|
100
|
+
// composite the mouse image ontop
|
|
101
|
+
sharpInstance.composite([{ input: cursorPath, left: mousePos.x, top: mousePos.y }]);
|
|
102
|
+
}
|
|
65
103
|
|
|
66
|
-
if (mouse) {
|
|
67
|
-
// composite the mouse image ontop
|
|
68
|
-
sharpInstance.composite([{ input: cursorPath, left: mousePos.x, top: mousePos.y }]);
|
|
69
104
|
}
|
|
70
105
|
|
|
71
106
|
await sharpInstance.toFile(step2);
|
|
@@ -115,7 +150,7 @@ const platform = () => {
|
|
|
115
150
|
// Import get-windows using dynamic import for ES module compatibility
|
|
116
151
|
let activeWindowFn = null;
|
|
117
152
|
const initializeActiveWindow = async () => {
|
|
118
|
-
if (!activeWindowFn) {
|
|
153
|
+
if (!activeWindowFn && !config.TD_VM) {
|
|
119
154
|
const { activeWindow } = await import('get-windows');
|
|
120
155
|
activeWindowFn = activeWindow;
|
|
121
156
|
}
|
|
@@ -124,12 +159,21 @@ const initializeActiveWindow = async () => {
|
|
|
124
159
|
|
|
125
160
|
// this is the focused window
|
|
126
161
|
const activeWin = async () => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
162
|
+
|
|
163
|
+
if (config.TD_VM) {
|
|
164
|
+
|
|
165
|
+
return "error getting active window, proceed normally";
|
|
166
|
+
|
|
167
|
+
} else {
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const activeWindow = await initializeActiveWindow();
|
|
171
|
+
return await activeWindow();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
logger.error('Error getting active window: %s', error);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
133
177
|
}
|
|
134
178
|
};
|
|
135
179
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const WebSocket = require('ws');
|
|
2
|
+
|
|
3
|
+
class WebSocketServerSingleton {
|
|
4
|
+
constructor() {
|
|
5
|
+
if (!WebSocketServerSingleton.instance) {
|
|
6
|
+
this.wss = new WebSocket.Server({ port: 8080 }, () => {
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
this.clients = new Set();
|
|
10
|
+
this.eventListeners = new Map();
|
|
11
|
+
|
|
12
|
+
this.wss.on('error', (error) => {
|
|
13
|
+
console.error('WebSocket server error:', error);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
this.wss.on('connection', (ws) => {
|
|
17
|
+
|
|
18
|
+
console.log('Client connected');
|
|
19
|
+
this.clients.add(ws);
|
|
20
|
+
|
|
21
|
+
ws.on('message', (message) => {
|
|
22
|
+
try {
|
|
23
|
+
const parsedMessage = JSON.parse(message);
|
|
24
|
+
if (parsedMessage.event) {
|
|
25
|
+
this.triggerEvent(parsedMessage.event, parsedMessage);
|
|
26
|
+
} else {
|
|
27
|
+
console.error('Parsed message does not contain an event property');
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Error parsing message:', error);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
ws.on('close', () => {
|
|
35
|
+
this.clients.delete(ws);
|
|
36
|
+
console.log('Client disconnected');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
ws.on('error', (error) => {
|
|
40
|
+
console.error('WebSocket error:', error);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
WebSocketServerSingleton.instance = this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return WebSocketServerSingleton.instance;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addEventListener(event, listener) {
|
|
51
|
+
if (!this.eventListeners.has(event)) {
|
|
52
|
+
this.eventListeners.set(event, []);
|
|
53
|
+
}
|
|
54
|
+
this.eventListeners.get(event).push(listener);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
triggerEvent(event, data) {
|
|
58
|
+
if (this.eventListeners.has(event)) {
|
|
59
|
+
for (const listener of this.eventListeners.get(event)) {
|
|
60
|
+
listener(data);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
sendToClients(event, message) {
|
|
66
|
+
const data = JSON.stringify({ event, message });
|
|
67
|
+
for (const client of this.clients) {
|
|
68
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
69
|
+
client.send(data);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const instance = new WebSocketServerSingleton();
|
|
76
|
+
Object.freeze(instance);
|
|
77
|
+
|
|
78
|
+
module.exports = instance;
|