testdriverai 4.1.12 → 4.1.13

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 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
- await delay(3000);
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();
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
- await analytics.track("command", { data: object, depth, timing });
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/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
- console.log("");
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 consoleOutput = markedParsePartial(buffer);
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/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
- console.log(` `, chalk.dim('redraw='), redrawText, chalk.dim('network='), networkText, chalk.dim('timeout='), timeoutText);
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
- console.log('')
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
- console.log("")
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 package = require("../package.json");
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 = await response.text();
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 fetch(url, config);
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", "v" + version, "testdriver", path].join("/");
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
- body: JSON.stringify({
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
- const response = await fetch(url, config);
118
- if (response.status === 301) {
119
- const redirectUrl = await response.text();
120
- return req(redirectUrl, data, onChunk);
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
- let chunk = new TextDecoder().decode(value);
134
+ await new Promise((resolve, reject) => {
142
135
 
143
- result += chunk;
144
- let events = [chunk];
145
- if (isJsonl) {
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
- events = lines
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
- if (isJsonl) {
160
- const events = result
161
- .split("\n")
162
- .slice(lastLineIndex + 1)
163
- .filter((line) => line.length)
164
- .map((line) => JSON.parse(line));
165
- for (const event of events) {
166
- await onChunk(event);
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.12",
3
+ "version": "4.1.13",
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",