testdriverai 7.2.2 → 7.2.9

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,12 +1,13 @@
1
- name: Publish Beta
1
+ name: Publish
2
2
  permissions:
3
3
  contents: write
4
+ id-token: write # Required for OIDC
4
5
  on:
5
6
  push:
6
7
  branches: [ main ]
7
8
 
8
9
  jobs:
9
- publish-beta:
10
+ publish:
10
11
  runs-on: ubuntu-latest
11
12
 
12
13
  steps:
@@ -29,16 +30,23 @@ jobs:
29
30
  - name: Install dependencies
30
31
  run: npm ci
31
32
 
32
- - name: Bump version (prerelease beta)
33
- run: npm version prerelease --preid=beta --no-git-tag-version
33
+ - name: Bump version (patch)
34
+ run: npm version patch --no-git-tag-version
34
35
 
35
36
  - name: Commit and push version bump
36
37
  run: |
37
38
  git add package.json package-lock.json
38
- git commit -m "chore: bump beta version to $(node -p "require('./package.json').version")"
39
+ git commit -m "chore: bump version to $(node -p "require('./package.json').version")"
39
40
  git push
40
41
 
41
- - name: Publish to npm under beta tag
42
- run: npm publish --tag beta
42
+ - name: Debug NPM Token
43
+ run: |
44
+ echo "NPM_TOKEN is set: ${{ secrets.NPM_TOKEN != '' }}"
45
+ echo "NPM_TOKEN first 4 chars: ${NPM_TOKEN:0:4}..."
46
+ env:
47
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
48
+
49
+ - name: Publish to npm
50
+ run: npm publish --tag beta
43
51
  env:
44
52
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,36 @@
1
+ name: TestDriver.ai Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, master ]
6
+ pull_request:
7
+ branches: [ main, master ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: '20'
20
+ cache: 'npm'
21
+
22
+ - name: Install dependencies
23
+ run: npm ci
24
+
25
+ - name: Run TestDriver.ai tests
26
+ env:
27
+ TD_API_KEY: ${{ secrets.TD_API_KEY }}
28
+ run: npx vitest run
29
+
30
+ - name: Upload test results
31
+ if: always()
32
+ uses: actions/upload-artifact@v4
33
+ with:
34
+ name: test-results
35
+ path: test-results/
36
+ retention-days: 30
package/agent/index.js CHANGED
@@ -17,7 +17,6 @@ const diff = require("diff");
17
17
 
18
18
  // global utilities
19
19
  const generator = require("./lib/generator.js");
20
- const promptCache = require("./lib/cache.js");
21
20
  const theme = require("./lib/theme.js");
22
21
  const SourceMapper = require("./lib/source-mapper.js");
23
22
 
@@ -110,6 +109,10 @@ class TestDriverAgent extends EventEmitter2 {
110
109
  // Create sandbox instance with this agent's emitter, analytics, and session
111
110
  this.sandbox = createSandbox(this.emitter, this.analytics, this.session);
112
111
 
112
+ // Attach Sentry log listeners to capture CLI logs as breadcrumbs
113
+ const sentry = require("../lib/sentry");
114
+ sentry.attachLogListeners(this.emitter);
115
+
113
116
  // Set the OS for the sandbox to use
114
117
  this.sandbox.os = this.sandboxOs;
115
118
 
@@ -191,6 +194,15 @@ class TestDriverAgent extends EventEmitter2 {
191
194
  this.redraw.cleanup();
192
195
  }
193
196
 
197
+ // Close sandbox connection to release the connection slot
198
+ if (this.sandbox) {
199
+ try {
200
+ this.sandbox.close();
201
+ } catch (err) {
202
+ // Ignore sandbox close errors during exit
203
+ }
204
+ }
205
+
194
206
  shouldRunPostrun =
195
207
  !this.hasRunPostrun &&
196
208
  (shouldRunPostrun || this.cliArgs?.command == "run");
@@ -356,7 +368,7 @@ class TestDriverAgent extends EventEmitter2 {
356
368
  image,
357
369
  },
358
370
  (chunk) => {
359
- if (chunk.type === "data") {
371
+ if (chunk.type === "data" && chunk.data) {
360
372
  this.emitter.emit(events.log.markdown.chunk, streamId, chunk.data);
361
373
  }
362
374
  },
@@ -420,9 +432,6 @@ class TestDriverAgent extends EventEmitter2 {
420
432
  let mousePosition = await this.system.getMousePosition();
421
433
  let activeWindow = await this.system.activeWin();
422
434
 
423
- const streamId = `check-${Date.now()}`;
424
- this.emitter.emit(events.log.markdown.start, streamId);
425
-
426
435
  let response = await this.sdk.req(
427
436
  "check",
428
437
  {
@@ -430,15 +439,10 @@ class TestDriverAgent extends EventEmitter2 {
430
439
  images,
431
440
  mousePosition,
432
441
  activeWindow,
433
- },
434
- (chunk) => {
435
- if (chunk.type === "data") {
436
- this.emitter.emit(events.log.markdown.chunk, streamId, chunk.data);
437
- }
438
- },
442
+ }
439
443
  );
440
444
 
441
- this.emitter.emit(events.log.markdown.end, streamId);
445
+ this.emitter.emit(events.log.markdown.static, response.data);
442
446
 
443
447
  this.lastScreenshot = thisScreenshot;
444
448
 
@@ -869,8 +873,7 @@ commands:
869
873
  currentTask,
870
874
  dry = false,
871
875
  validateAndLoop = false,
872
- shouldSave = true,
873
- useCache = true,
876
+ shouldSave = true
874
877
  ) {
875
878
  // Check if execution has been stopped
876
879
  if (this.stopped) {
@@ -889,56 +892,10 @@ commands:
889
892
 
890
893
  this.tasks.push(currentTask);
891
894
 
892
- // Check cache first (if enabled via parameter)
893
- const cachedYaml = useCache ? promptCache.readCache(currentTask) : null;
894
-
895
- if (cachedYaml) {
896
- // Cache hit - load and execute the cached YAML file
897
- this.emitter.emit(
898
- events.log.debug,
899
- `Using cached response for prompt: "${currentTask}"`,
900
- );
901
- this.emitter.emit(events.log.log, theme.dim("(using cached response)"));
902
-
903
- try {
904
- // Load the YAML using hydrateFromYML
905
- const parsed = await generator.hydrateFromYML(
906
- cachedYaml,
907
- this.sessionInstance,
908
- );
909
-
910
- // Execute the commands from the first step
911
- if (parsed.steps && parsed.steps.length > 0) {
912
- const step = parsed.steps[0];
913
- if (step.commands) {
914
- await this.executeCommands(
915
- step.commands,
916
- 0,
917
- false,
918
- dry,
919
- shouldSave,
920
- );
921
- }
922
- }
923
- } catch (err) {
924
- this.emitter.emit(
925
- events.log.debug,
926
- `Error loading cached YAML: ${err.message}, falling back to API`,
927
- );
928
- // Fall through to make API call if cache is invalid
929
- }
930
-
931
- return;
932
- }
933
-
934
- // Cache miss - call the API
935
895
  this.emitter.emit(events.log.narration, theme.dim("thinking..."), true);
936
896
 
937
897
  this.lastScreenshot = await this.system.captureScreenBase64();
938
898
 
939
- const streamId = `input-${Date.now()}`;
940
- this.emitter.emit(events.log.markdown.start, streamId);
941
-
942
899
  let message = await this.sdk.req(
943
900
  "input",
944
901
  {
@@ -946,59 +903,12 @@ commands:
946
903
  mousePosition: await this.system.getMousePosition(),
947
904
  activeWindow: await this.system.activeWin(),
948
905
  image: this.lastScreenshot,
949
- },
950
- (chunk) => {
951
- if (chunk.type === "data") {
952
- this.emitter.emit(events.log.markdown.chunk, streamId, chunk.data);
953
- }
954
- },
906
+ }
955
907
  );
956
908
 
957
- this.emitter.emit(events.log.markdown.end, streamId);
909
+ this.emitter.emit(events.log.log, message.data);
958
910
 
959
911
  if (message && message.data) {
960
- // Save the YAML to cache (if enabled)
961
- if (useCache) {
962
- try {
963
- // Extract YAML code blocks from the markdown response
964
- const codeblocks = await this.parser.findCodeBlocks(message.data);
965
- if (codeblocks && codeblocks.length > 0) {
966
- // Parse commands from all code blocks
967
- const allCommands = [];
968
- for (const block of codeblocks) {
969
- const commands = await this.parser.getCommands(block);
970
- allCommands.push(...commands);
971
- }
972
-
973
- // Create a proper step with prompt
974
- const step = {
975
- prompt: currentTask,
976
- commands: allCommands,
977
- };
978
-
979
- // Use dumpToYML to create a valid testdriver yaml file
980
- const yamlContent = await generator.dumpToYML(
981
- [step],
982
- this.sessionInstance,
983
- );
984
-
985
- const cachePath = promptCache.writeCache(currentTask, yamlContent);
986
- if (cachePath) {
987
- this.emitter.emit(
988
- events.log.debug,
989
- `Cached YAML saved to: ${cachePath}`,
990
- );
991
- }
992
- }
993
- } catch (err) {
994
- // If we can't extract YAML, just skip caching
995
- this.emitter.emit(
996
- events.log.debug,
997
- `Could not cache response: ${err.message}`,
998
- );
999
- }
1000
- }
1001
-
1002
912
  await this.aiExecute(message.data, validateAndLoop, dry, shouldSave);
1003
913
  this.emitter.emit(
1004
914
  events.log.debug,
@@ -2200,6 +2110,15 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2200
2110
  }
2201
2111
 
2202
2112
  this.session.set(sessionRes.data.id);
2113
+
2114
+ // Set Sentry session trace context for distributed tracing
2115
+ // This links CLI errors/logs to the same trace as API calls
2116
+ try {
2117
+ const sentry = require("../lib/sentry");
2118
+ sentry.setSessionTraceContext(sessionRes.data.id);
2119
+ } catch (e) {
2120
+ // Sentry module may not be available, ignore
2121
+ }
2203
2122
  }
2204
2123
 
2205
2124
  // Helper method to find testdriver directory by traversing up from a file path
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // Initialize Sentry first, before any other modules
4
+ const sentry = require("../lib/sentry");
5
+
3
6
  // Set process priority if possible
4
7
  const os = require("os");
5
8
  try {
@@ -10,5 +13,10 @@ try {
10
13
  // Ignore if not permitted
11
14
  }
12
15
 
16
+ // Ensure Sentry flushes on exit
17
+ process.on("beforeExit", async () => {
18
+ await sentry.flush();
19
+ });
20
+
13
21
  // Run the CLI
14
22
  require("../interfaces/cli.js");
@@ -307,9 +307,46 @@
307
307
  text-align: center;
308
308
  user-select: none;
309
309
  }
310
+
311
+ .close-button {
312
+ position: fixed;
313
+ top: 12px;
314
+ right: 12px;
315
+ z-index: 100;
316
+ background: rgba(0, 0, 0, 0.8);
317
+ border: 1px solid #444;
318
+ color: #fff;
319
+ padding: 8px 16px;
320
+ border-radius: 6px;
321
+ cursor: pointer;
322
+ font-size: 13px;
323
+ font-weight: 500;
324
+ pointer-events: auto;
325
+ transition: all 0.2s ease;
326
+ display: flex;
327
+ align-items: center;
328
+ gap: 6px;
329
+ }
330
+
331
+ .close-button:hover {
332
+ background: rgba(220, 53, 69, 0.9);
333
+ border-color: #dc3545;
334
+ }
335
+
336
+ .close-button svg {
337
+ width: 14px;
338
+ height: 14px;
339
+ fill: currentColor;
340
+ }
310
341
  </style>
311
342
  </head>
312
343
  <body>
344
+ <!-- Close window button -->
345
+ <button class="close-button" onclick="window.close()" title="Close this window">
346
+ <svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
347
+ Close
348
+ </button>
349
+
313
350
  <!-- Loading screen -->
314
351
  <div class="loading-screen" id="loading-screen">
315
352
  <div class="testdriver-logo">
package/docs/docs.json CHANGED
@@ -192,15 +192,8 @@
192
192
  "groups": [
193
193
  {
194
194
  "group": "Getting Started",
195
- "icon": "rocket",
196
- "pages": [
197
- "/v7/getting-started/quickstart"
198
- ]
199
- },
200
- {
201
- "group": "Guides",
202
- "icon": "book",
203
195
  "pages": [
196
+ "/v7/getting-started/quickstart",
204
197
  "/v7/getting-started/writing-tests",
205
198
  "/v7/getting-started/running-tests",
206
199
  "/v7/getting-started/debugging-tests",
@@ -209,7 +202,6 @@
209
202
  },
210
203
  {
211
204
  "group": "Examples",
212
- "icon": "code",
213
205
  "pages": [
214
206
  "/v7/presets/chrome",
215
207
  "/v7/presets/chrome-extension",
@@ -218,8 +210,7 @@
218
210
  ]
219
211
  },
220
212
  {
221
- "group": "Features",
222
- "icon": "layer-group",
213
+ "group": "Guides",
223
214
  "pages": [
224
215
  {
225
216
  "group": "Selectorless Testing",
@@ -44,7 +44,6 @@ This system provides comprehensive test execution tracking, linking test runs wi
44
44
  │ │ │ │
45
45
  │ │ • TdTestRun │ │
46
46
  │ │ • TdTestCase │ │
47
- │ │ • TdSandbox │ │
48
47
  │ │ • Replay │ │
49
48
  │ └────────────────┘ │
50
49
  │ │
@@ -95,7 +94,6 @@ Represents a complete test suite execution (e.g., `npx vitest run`).
95
94
 
96
95
  **Relationships:**
97
96
  - `team`: Owner team
98
- - `sandbox`: TdSandbox where tests ran
99
97
  - `testCases`: Collection of TdTestCase
100
98
  - `replays`: Associated Replay records
101
99
 
@@ -114,36 +112,13 @@ Represents an individual test within a test run.
114
112
 
115
113
  **Relationships:**
116
114
  - `testRun`: Parent TdTestRun
117
- - `replay`: Associated Replay record
118
-
119
- ### TdSandbox
120
- Represents a spawned VM/sandbox instance.
121
-
122
- **Key Fields:**
123
- - `sandboxId`: Unique identifier
124
- - `platform`: windows | mac | linux
125
- - `status`: provisioning | ready | running | stopped | terminated
126
- - `instanceId`, `instanceType`: AWS EC2 details
127
- - `ipAddress`, `vncUrl`, `wsUrl`: Connection details
128
- - `spawnTime`, `readyTime`, `terminateTime`: Lifecycle timestamps
129
- - `dashcamAuth`: Whether dashcam was authenticated
130
- - `dashcamProjectId`: Dashcam project for replays
131
-
132
- **Relationships:**
133
- - `team`: Owner team
134
- - `user`: User who spawned it
135
- - `testRuns`: Tests that ran on this sandbox
136
- - `replays`: Dashcam recordings from this sandbox
137
-
138
- **Note:** Sandbox creation/updates happen via WebSocket (not REST API) as part of the sandbox provisioning flow.
139
-
115
+ - `replay`: Associated Replay recor
140
116
  ### Replay (Extended)
141
117
  Existing model extended with test run associations.
142
118
 
143
119
  **New Fields:**
144
120
  - `tdTestRun`: Associated test run
145
121
  - `tdTestCase`: Associated test case
146
- - `tdSandbox`: Sandbox where recorded
147
122
 
148
123
  ## API Endpoints
149
124