testdriverai 4.2.20 → 5.0.0-beta.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.
@@ -1,7 +1,7 @@
1
1
  # To get started with Dependabot version updates, you'll need to specify which
2
2
  # package ecosystems to update and where the package manifests are located.
3
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
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yaml-file
5
5
 
6
6
  version: 2
7
7
  updates:
@@ -17,7 +17,7 @@ jobs:
17
17
  branch: main
18
18
  key: ${{secrets.TESTDRIVER_API_KEY}}
19
19
  prompt: |
20
- 1. /run test.yml
20
+ 1. /run test.yaml
21
21
  prerun: |
22
22
  Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "${{ env.WEBSITE_URL }}"
23
23
 
@@ -0,0 +1,28 @@
1
+ name: TestDriver.ai
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ test:
11
+ name: "TestDriver"
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: testdriverai/action@main
15
+ with:
16
+ key: ${{secrets.TESTDRIVER_API_KEY}}
17
+ prompt: |
18
+ 1. /run testdriver/testdriver.yml
19
+ prerun: |
20
+ cd $env:TEMP
21
+ npm init -y
22
+ npm install dashcam-chrome
23
+ Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--load-extension=$(pwd)/node_modules/dashcam-chrome/build", "${{ env.WEBSITE_URL }}"
24
+ exit
25
+ env:
26
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
+ FORCE_COLOR: "3"
28
+ WEBSITE_URL: "https://example.com" # Define the website URL here
package/README.md CHANGED
@@ -60,7 +60,7 @@ npm install testdriverai -g
60
60
  Let's show TestDriver what we want to test. Run the following command:
61
61
 
62
62
  ```sh
63
- testdriverai .testdriver/test.yml
63
+ testdriverai .testdriver/test.yaml
64
64
  ```
65
65
 
66
66
  ## Reset the test state
@@ -131,7 +131,7 @@ If something didn't work, you can use `/undo` to remove all of the test steps ad
131
131
  Now it's time to make sure the test plan works before we deploy it. Use testdriver run to run the test file you just created with /save .
132
132
 
133
133
  ```sh
134
- testdriverai run testdriver/test.yml
134
+ testdriverai run testdriver/test.yaml
135
135
  ```
136
136
 
137
137
  Make sure to reset the test state!
@@ -147,3 +147,4 @@ gh pr create --web
147
147
  ```
148
148
 
149
149
  Your test will run on every commit and the results will be posted as a Dashcam.io video within your GitHub summary! Learn more about deploying on CI [here](https://docs.testdriver.ai/continuous-integration/overview).
150
+ # vscode
package/agent.js CHANGED
@@ -31,6 +31,7 @@ const sanitizeFilename = require("sanitize-filename");
31
31
  const macScreenPerms = require("mac-screen-capture-permissions");
32
32
 
33
33
  // local modules
34
+ const wss = require("./lib/websockets.js");
34
35
  const speak = require("./lib/speak.js");
35
36
  const analytics = require("./lib/analytics.js");
36
37
  const log = require("./lib/logger.js");
@@ -42,6 +43,7 @@ const sdk = require("./lib/sdk.js");
42
43
  const commands = require("./lib/commands.js");
43
44
  const init = require("./lib/init.js");
44
45
  const config = require("./lib/config.js");
46
+ const sandbox = require("./lib/sandbox.js");
45
47
 
46
48
  const { showTerminal, hideTerminal } = require("./lib/focus-application.js");
47
49
  const isValidVersion = require("./lib/valid-version.js");
@@ -106,14 +108,14 @@ let getArgs = () => {
106
108
  fs.mkdirSync(testdriverFolder);
107
109
  }
108
110
 
109
- args[file] = "testdriver/testdriver.yml";
111
+ args[file] = "testdriver/testdriver.yaml";
110
112
  }
111
113
 
112
114
  // turn args[file] into local path
113
115
  if (args[file]) {
114
- args[file] = `${process.cwd()}/${args[file]}`;
115
- if (!args[file].endsWith(".yml")) {
116
- args[file] += ".yml";
116
+ args[file] = path.join(process.cwd(), args[file]);
117
+ if (!args[file].endsWith(".yaml")) {
118
+ args[file] += ".yaml";
117
119
  }
118
120
  }
119
121
 
@@ -167,7 +169,7 @@ function fileCompleter(line) {
167
169
  }
168
170
 
169
171
  function completer(line) {
170
- let completions = "/summarize /save /run /quit /assert /undo /manual".split(
172
+ let completions = "/summarize /save /run /quit /assert /undo /manual /yml".split(
171
173
  " ",
172
174
  );
173
175
  if (line.startsWith("/run ")) {
@@ -266,6 +268,7 @@ const haveAIResolveError = async (error, markdown, depth = 0, undo = true) => {
266
268
 
267
269
  speak("thinking...");
268
270
  notify("thinking...");
271
+
269
272
  logger.info(chalk.dim("thinking..."), true);
270
273
  logger.info("");
271
274
 
@@ -374,14 +377,16 @@ const runCommand = async (command, depth) => {
374
377
  let lastCommand = new Date().getTime();
375
378
  let csv = [["command,time"]];
376
379
 
377
- const executeCommands = async (commands, depth, pushToHistory = false) => {
380
+ const executeCommands = async (commands, depth, pushToHistory = false, dry = false) => {
378
381
  if (commands?.length) {
379
382
  for (const command of commands) {
380
383
  if (pushToHistory) {
381
384
  executionHistory[executionHistory.length - 1]?.commands.push(command);
382
385
  }
383
386
 
384
- await runCommand(command, depth);
387
+ if (!dry) {
388
+ await runCommand(command, depth);
389
+ }
385
390
 
386
391
  let timeToComplete = (new Date().getTime() - lastCommand) / 1000;
387
392
  // logger.info(timeToComplete, 'seconds')
@@ -394,7 +399,7 @@ const executeCommands = async (commands, depth, pushToHistory = false) => {
394
399
 
395
400
  // note that commands are run in a recursive loop, so that the AI can respond to the output of the commands
396
401
  // like `click-image` and `click-text` for example
397
- const executeCodeBlocks = async (codeblocks, depth, pushToHistory = false) => {
402
+ const executeCodeBlocks = async (codeblocks, depth, pushToHistory = false, dry = false) => {
398
403
  depth = depth + 1;
399
404
 
400
405
  logger.debug("%j", { message: "execute code blocks", depth });
@@ -412,20 +417,20 @@ const executeCodeBlocks = async (codeblocks, depth, pushToHistory = false) => {
412
417
  );
413
418
  }
414
419
 
415
- await executeCommands(commands, depth, pushToHistory);
420
+ await executeCommands(commands, depth, pushToHistory, dry);
416
421
  }
417
422
  };
418
423
 
419
424
  // this is the main function that interacts with the ai, runs commands, and checks the results
420
425
  // notice that depth is 0 here. when this function resolves, the task is considered complete
421
426
  // notice the call to `check()` which validates the prompt is complete
422
- const aiExecute = async (message, validateAndLoop = false) => {
427
+ const aiExecute = async (message, validateAndLoop = false, dry = false) => {
423
428
  executionHistory.push({ prompt: lastPrompt, commands: [] });
424
429
 
425
430
  logger.debug("kicking off exploratory loop");
426
-
431
+
427
432
  // kick everything off
428
- await actOnMarkdown(message, 0, true);
433
+ await actOnMarkdown(message, 0, true, dry);
429
434
 
430
435
  if (validateAndLoop) {
431
436
  logger.debug("exploratory loop resolved, check your work");
@@ -509,7 +514,7 @@ const assert = async (expect) => {
509
514
  logger.info(chalk.dim("thinking..."), true);
510
515
  logger.info("");
511
516
 
512
- let response = `\`\`\`yml
517
+ let response = `\`\`\`yaml
513
518
  commands:
514
519
  - command: assert
515
520
  expect: ${expect}
@@ -522,11 +527,11 @@ commands:
522
527
 
523
528
  // this function responds to the result of `promptUser()` which is the user input
524
529
  // it kicks off the exploratory loop, which is the main function that interacts with the AI
525
- const humanInput = async (currentTask, validateAndLoop = false) => {
530
+ const exploratoryLoop = async (currentTask, dry = false, validateAndLoop = false) => {
526
531
  lastPrompt = currentTask;
527
532
  checkCount = 0;
528
533
 
529
- logger.debug("humanInput called");
534
+ logger.debug("exploratoryLoop called");
530
535
 
531
536
  tasks.push(currentTask);
532
537
 
@@ -554,11 +559,11 @@ const humanInput = async (currentTask, validateAndLoop = false) => {
554
559
  );
555
560
  mdStream.end();
556
561
 
557
- await aiExecute(message.data, validateAndLoop);
562
+ await aiExecute(message.data, validateAndLoop, dry);
558
563
 
559
- logger.debug("showing prompt from humanInput response check");
564
+ logger.debug("showing prompt from exploratoryLoop response check");
560
565
 
561
- await save({ silent: true });
566
+ await save({ silent: false });
562
567
  };
563
568
 
564
569
  const generate = async (type, count, baseYaml, skipYaml = false) => {
@@ -663,7 +668,7 @@ ${yml}
663
668
  };
664
669
 
665
670
  // this function is responsible for starting the recursive process of executing codeblocks
666
- const actOnMarkdown = async (content, depth, pushToHistory = false) => {
671
+ const actOnMarkdown = async (content, depth, pushToHistory = false, dry = false) => {
667
672
  logger.debug("%j", {
668
673
  message: "actOnMarkdown called",
669
674
  depth,
@@ -678,7 +683,7 @@ const actOnMarkdown = async (content, depth, pushToHistory = false) => {
678
683
  }
679
684
 
680
685
  if (codeblocks.length) {
681
- let executions = await executeCodeBlocks(codeblocks, depth, pushToHistory);
686
+ let executions = await executeCodeBlocks(codeblocks, depth, pushToHistory, dry);
682
687
  return executions;
683
688
  } else {
684
689
  return true;
@@ -720,7 +725,7 @@ const firstPrompt = async () => {
720
725
 
721
726
  // this is how we parse user input
722
727
  // notice that the AI is only called if the input is not a command
723
- rl.on("line", async (input) => {
728
+ const handleInput = async (input) => {
724
729
  if (!isInteractive) return;
725
730
  if (!input.trim().length) return promptUser();
726
731
 
@@ -789,12 +794,22 @@ const firstPrompt = async () => {
789
794
  } else if (input.indexOf("/generate") == 0) {
790
795
  const skipYaml = commands[4] === "--skip-yaml";
791
796
  await generate(commands[1], commands[2], commands[3], skipYaml);
797
+ } else if (input.indexOf("/dry") == 0) {
798
+ await exploratoryLoop(input.replace('/dry', ''), true, false);
799
+ } else if (input.indexOf("/yaml") == 0) {
800
+ await runRawYML(commands[1]);
792
801
  } else {
793
- await humanInput(input, true);
802
+ await exploratoryLoop(input, false, true);
794
803
  }
795
804
 
796
805
  setTerminalWindowTransparency(false);
797
806
  promptUser();
807
+ };
808
+
809
+ rl.on("line", handleInput);
810
+
811
+ wss.addEventListener("input", async (message) => {
812
+ handleInput(message.data);
798
813
  });
799
814
 
800
815
  // if file exists, load it
@@ -923,11 +938,12 @@ let save = async ({ filepath = thisFile, silent = false } = {}) => {
923
938
  analytics.track("save", { silent });
924
939
 
925
940
  if (!silent) {
926
- logger.info(chalk.dim("saving..."), true);
941
+ logger.info(chalk.dim(`saving as ${filepath}...`), true);
927
942
  logger.info("");
928
943
  }
929
944
 
930
945
  if (!executionHistory.length) {
946
+ console.log('no exeuction history, not saving')
931
947
  return;
932
948
  }
933
949
 
@@ -936,6 +952,7 @@ let save = async ({ filepath = thisFile, silent = false } = {}) => {
936
952
  try {
937
953
  fs.writeFileSync(filepath, regression);
938
954
  } catch (e) {
955
+ console.log(e)
939
956
  logger.error(e.message);
940
957
  logger.error("%s", e);
941
958
  }
@@ -956,15 +973,33 @@ ${regression}
956
973
  }
957
974
  };
958
975
 
976
+ let runRawYML = async (yml) => {
977
+
978
+ const tmp = require("tmp");
979
+ let tmpobj = tmp.fileSync();
980
+
981
+ let decoded = decodeURIComponent(yml);
982
+
983
+ // saved the yml to a temp file using tmp
984
+ // and run it with run()
985
+ fs.writeFileSync(tmpobj.name, await generator.rawToFormatted(decoded));
986
+
987
+ await run(tmpobj.name, false, true);
988
+
989
+ }
990
+
959
991
  // this will load a regression test from a file location
960
992
  // it parses the markdown file and executes the codeblocks exactly as if they were
961
993
  // generated by the AI in a single prompt
962
- let run = async (file, shouldSave = false, shouldExit = true) => {
994
+ let run = async (file = thisFile, shouldSave = false, shouldExit = true) => {
963
995
  await newSession();
964
996
 
965
997
  setTerminalWindowTransparency(true);
966
998
  emitter.emit(events.interactive, false);
967
999
 
1000
+ // get the current wowrking directory where this file is being executed
1001
+ let cwd = process.cwd();
1002
+
968
1003
  logger.info(chalk.cyan(`running ${file}...`));
969
1004
 
970
1005
  let ymlObj = await loadYML(file);
@@ -1017,6 +1052,7 @@ ${yaml.dump(step)}
1017
1052
  };
1018
1053
 
1019
1054
  const promptUser = () => {
1055
+ wss.sendToClients("done");
1020
1056
  emitter.emit(events.interactive, true);
1021
1057
  rl.prompt(true);
1022
1058
  };
@@ -1122,9 +1158,11 @@ const start = async () => {
1122
1158
  analytics.track("command", { command: thisCommand, file: thisFile });
1123
1159
 
1124
1160
  if (thisCommand == "edit") {
1161
+ await makeSandbox();
1125
1162
  firstPrompt();
1126
1163
  } else if (thisCommand == "run") {
1127
1164
  errorLimit = 100;
1165
+ await makeSandbox();
1128
1166
  run(thisFile);
1129
1167
  } else if (thisCommand == "init") {
1130
1168
  await init();
@@ -1132,6 +1170,34 @@ const start = async () => {
1132
1170
  }
1133
1171
  };
1134
1172
 
1173
+ const makeSandbox = async () => {
1174
+
1175
+ if (config.TD_VM) {
1176
+
1177
+ logger.info(chalk.gray(`- creating linux sandbox...`));
1178
+ await sandbox.boot();
1179
+ logger.info(chalk.gray(`- authenticating...`));
1180
+ await sandbox.send({type: 'authenticate', apiKey: config.TD_API_KEY });
1181
+ logger.info(chalk.gray(`- setting up...`));
1182
+ await sandbox.send({type: 'create', resolution: [1024, 768]});
1183
+ logger.info(chalk.gray(`- starting stream...`));
1184
+ await sandbox.send({type: 'stream.start'});
1185
+ let {url} = await sandbox.send({type: 'stream.getUrl'});
1186
+ logger.info(chalk.gray(`- rendering...`));
1187
+ await sandbox.send({type: 'ready'});
1188
+ logger.info(chalk.gray(`- booting...`));
1189
+ await new Promise(resolve => setTimeout(resolve, 5000));
1190
+ logger.info(chalk.green(``));
1191
+ logger.info(chalk.green(`sandbox runner ready!`));
1192
+ emitter.emit(events.vm.show, {url});
1193
+
1194
+ }
1195
+
1196
+ emitter.emit(events.interactive, false);
1197
+ emitter.emit(events.showWindow)
1198
+
1199
+ }
1200
+
1135
1201
  process.on("uncaughtException", async (err) => {
1136
1202
  analytics.track("uncaughtException", { err });
1137
1203
  logger.error("Uncaught Exception: %s", err);
@@ -2,12 +2,20 @@
2
2
  <html>
3
3
 
4
4
  <head>
5
+ <link rel="preconnect" href="https://fonts.googleapis.com">
6
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
7
+ <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
8
+ <title>TestDriver</title>
5
9
  <style>
6
10
  * {
7
11
  margin: 0;
8
12
  padding: 0;
9
13
  overflow: hidden;
10
14
  box-sizing: border-box;
15
+ font-family: "IBM Plex Mono", monospace;
16
+ font-weight: 400;
17
+ font-style: normal;
18
+ font-size: 14px;
11
19
  }
12
20
 
13
21
  @keyframes animate-glow {
@@ -64,11 +72,14 @@
64
72
  position: relative;
65
73
  }
66
74
 
75
+ #main {
76
+ pointer-events: none;
77
+ }
78
+
67
79
  .screenshot {
68
80
  position: absolute;
69
81
  inset: 0;
70
- border: 8px dashed #B0CF34;
71
- border-radius: 20px;
82
+ z-index: 1;
72
83
  opacity: 0;
73
84
  animation-duration: 0.3s;
74
85
  animation-delay: 0;
@@ -77,6 +88,7 @@
77
88
  animation-name: animate-screenshot;
78
89
  animation-timing-function: ease;
79
90
  animation-fill-mode: forwards;
91
+ background-color: white;
80
92
  }
81
93
 
82
94
  .box {
@@ -90,6 +102,7 @@
90
102
  animation-name: animate-glow;
91
103
  animation-timing-function: ease;
92
104
  animation-fill-mode: forwards;
105
+ border-radius: 5px;
93
106
  }
94
107
 
95
108
  .container {
@@ -150,6 +163,7 @@
150
163
  }
151
164
 
152
165
  #terminal-wrapper {
166
+ pointer-events: none;
153
167
  position: absolute;
154
168
  left: calc(100vw - 700px);
155
169
  top: 0px;
@@ -159,7 +173,7 @@
159
173
  opacity: 0;
160
174
  z-index: -1;
161
175
  overflow-y: auto;
162
- padding: 4px;
176
+ padding: 20px;
163
177
  }
164
178
 
165
179
  #terminal {
@@ -169,11 +183,22 @@
169
183
  }
170
184
 
171
185
  #boxes {
186
+ pointer-events: none;
172
187
  position: absolute;
173
188
  top: 0;
174
189
  left: 0;
175
190
  right: 0;
176
191
  bottom: 0;
192
+ z-index: 2;
193
+ }
194
+
195
+ #vm-iframe {
196
+ position: absolute;
197
+ top: 0;
198
+ left: 0;
199
+ width: 100%;
200
+ height: 100%;
201
+ border: none;
177
202
  }
178
203
  </style>
179
204
  <link rel="stylesheet" href="terminal/xterm.css" />
@@ -182,15 +207,19 @@
182
207
  </head>
183
208
 
184
209
  <body>
210
+
185
211
  <div id="screenshot"></div>
186
212
  <div id="main" class="container">
187
213
  <div id="boxes">
188
214
  <div id="pointer"></div>
189
215
  </div>
190
216
  <div id="terminal-wrapper">
217
+ <img src="td.png" alt="td" style="position: absolute; top: 20; right: 20; height: 40px; z-index: 9999; background-color: black;">
191
218
  <div id="terminal"></div>
192
219
  </div>
193
220
  </div>
221
+ <iframe id="vm-iframe" frameborder="0"></iframe>
222
+
194
223
  <script>
195
224
 
196
225
  const { ipcRenderer } = require("electron");
@@ -226,7 +255,6 @@
226
255
  });
227
256
  }
228
257
 
229
-
230
258
  ipcRenderer.on(events.screenCapture.start, (event, data) => {
231
259
  if (data?.silent) return
232
260
  // Hide everything whlie the app takes the screenshot
@@ -279,7 +307,7 @@
279
307
  cursorInactiveStyle: "none",
280
308
  scrollback: 9999999,
281
309
  allowProposedApi: true,
282
- fontSize: 18
310
+ fontSize: 14
283
311
  });
284
312
 
285
313
  const fitAddon = new FitAddon.FitAddon();
@@ -287,6 +315,11 @@
287
315
  terminal.open(terminalElement);
288
316
  fitAddon.fit();
289
317
 
318
+ ipcRenderer.on(events.vm.show, (event, data) => {
319
+ const iframe = document.querySelector("#vm-iframe");
320
+ iframe.src = data.url;
321
+ });
322
+
290
323
  ipcRenderer.on(events.terminal.stdout, (event, data) =>
291
324
  terminal.write(data),
292
325
  );
@@ -1,6 +1,7 @@
1
1
  const ipc = require("node-ipc").default;
2
2
  const { app, screen, BrowserWindow } = require("electron");
3
3
  const { eventsArray } = require("../lib/events.js");
4
+ const config = require("../lib/config.js");
4
5
 
5
6
  ipc.config.id = "testdriverai_overlay";
6
7
  ipc.config.retry = 1500;
@@ -9,47 +10,79 @@ ipc.config.silent = true;
9
10
  app.whenReady().then(() => {
10
11
  app.dock?.hide();
11
12
 
12
- const windowOptions = {
13
- ...screen.getPrimaryDisplay().bounds,
14
- enableLargerThanScreen: true,
15
- frame: false,
16
- show: false,
17
- closable: false,
18
- resizable: false,
19
- focusable: false,
20
- fullscreenable: true,
21
- transparent: true,
22
- alwaysOnTop: true,
23
- skipTaskbar: true,
24
- webPreferences: {
25
- nodeIntegration: true,
26
- contextIsolation: false,
27
- },
28
- autoHideMenuBar: true,
29
- };
13
+ let windowOptions;
14
+
15
+ if (config.TD_VM) {
16
+
17
+ windowOptions = {
18
+ width: 1030,
19
+ height: 800,
20
+ closable: true,
21
+ resizable: true,
22
+ show: false,
23
+ alwaysOnTop: true,
24
+ webPreferences: {
25
+ nodeIntegration: true,
26
+ contextIsolation: false,
27
+ },
28
+ autoHideMenuBar: true,
29
+ };
30
+
31
+ } else {
32
+
33
+ windowOptions = {
34
+ ...screen.getPrimaryDisplay().bounds,
35
+ closable: true,
36
+ resizable: true,
37
+ alwaysOnTop: true,
38
+ enableLargerThanScreen: true,
39
+ frame: false,
40
+ show: false,
41
+ focusable: false,
42
+ fullscreenable: true,
43
+ transparent: true,
44
+ skipTaskbar: true,
45
+ webPreferences: {
46
+ nodeIntegration: true,
47
+ contextIsolation: false,
48
+ },
49
+ autoHideMenuBar: true,
50
+ };
51
+
52
+ }
30
53
 
31
54
  if (process.platform !== 'darwin') {
32
55
  windowOptions.fullscreen = true;
33
56
  }
34
57
 
35
58
  const window = new BrowserWindow(windowOptions);
36
- window.setIgnoreMouseEvents(true);
37
- window.setAlwaysOnTop(true, "screen-saver");
38
- window.setVisibleOnAllWorkspaces(true, {
39
- visibleOnFullScreen: true,
40
- });
59
+
60
+ if (!config.TD_VM) {
61
+ window.setIgnoreMouseEvents(true);
62
+ window.setAlwaysOnTop(true, "screen-saver");
63
+ window.setVisibleOnAllWorkspaces(true, {
64
+ visibleOnFullScreen: true,
65
+ });
66
+ } else {
67
+ window.setBackgroundColor('#000')
68
+ }
69
+
41
70
  window.loadFile("overlay.html");
42
71
 
43
72
  window.once('ready-to-show', () => {
44
- window.showInactive();
73
+ // window.showInactive();
45
74
  });
46
75
 
47
76
  // open developer tools
48
- // window.webContents.openDevTools();
77
+ window.webContents.openDevTools();
49
78
 
50
79
  ipc.serve(() => {
51
80
  for (const event of eventsArray) {
52
81
  ipc.server.on(event, (data) => {
82
+ if (event === "show-window") {
83
+ window.showInactive();
84
+ return;
85
+ }
53
86
  window?.webContents.send(event, data);
54
87
  });
55
88
  }
Binary file
package/index.js CHANGED
@@ -13,6 +13,7 @@ const { logger } = require("./lib/logger.js");
13
13
  agent.setTerminalApp(win);
14
14
  agent.start();
15
15
  } else {
16
+
16
17
  // Intercept all stdout and stderr calls (works with console as well)
17
18
  const originalStdoutWrite = process.stdout.write.bind(process.stdout);
18
19
  process.stdout.write = (...args) => {
@@ -35,9 +36,8 @@ const { logger } = require("./lib/logger.js");
35
36
  };
36
37
 
37
38
  require("./lib/overlay.js")
38
- .electronProcessPromise.then(() => {
39
+ .electronProcessPromise.then(async () => {
39
40
  let agent = require("./agent.js");
40
- agent.setTerminalApp(win);
41
41
  agent.start();
42
42
  })
43
43
  .catch((err) => {
package/lib/commander.js CHANGED
@@ -25,6 +25,9 @@ const replaceOutputs = (obj) => {
25
25
  // object is a json representation of the individual yml command
26
26
  // the process turns markdown -> yml -> json -> js function execution
27
27
  const run = async (object, depth) => {
28
+
29
+ try {
30
+
28
31
  logger.debug("%j", { object, depth });
29
32
 
30
33
  // success returns null
@@ -44,7 +47,7 @@ Our test structure is in YAML format.
44
47
 
45
48
  Here is a simple example:
46
49
 
47
- \`\`\`yml
50
+ \`\`\`yaml
48
51
  commands:
49
52
  - command: press-keys
50
53
  keys: [command, space]
@@ -216,6 +219,11 @@ commands:
216
219
  ]);
217
220
 
218
221
  return response;
219
- };
222
+
223
+ } catch (error) {
224
+ logger.error(error);
225
+ throw error;
226
+ }
227
+ }
220
228
 
221
229
  module.exports = { run };