testdriverai 7.5.15 → 7.5.17

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/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 7.5.17 (2026-03-07)
2
+
3
+ ## ✨ Features
4
+ - Enhanced marketing website with new "How It Works" section and improved visual components (161c1c7b)
5
+ - Added new brand assets and workflow illustrations for better user onboarding (161c1c7b)
6
+
7
+ ## 🐛 Bug Fixes
8
+ - Fixed Stripe checkout pricing and authentication issues [API] (161c1c7b)
9
+ - Resolved potential witness functionality issues [SDK] (161c1c7b)
10
+
11
+ ## 🔧 Maintenance
12
+ - Updated SDK documentation examples with improved formatting and generation scripts [SDK] (161c1c7b)
13
+ - Enhanced sandbox authentication and SDK functionality [SDK] (161c1c7b)
14
+ - Improved development environment configuration and tooling (161c1c7b)
15
+
16
+ ## 7.5.16 (2026-03-07)
17
+
18
+ ## ✨ Features
19
+
20
+ - Updated pricing structure and checkout flow for improved billing experience [API] (7821df39)
21
+
22
+ ## 🐛 Bug Fixes
23
+
24
+ - Fixed Stripe integration issues affecting payment processing [API] (7821df39)
25
+
1
26
  ## 7.5.15 (2026-03-07)
2
27
 
3
28
  ## 🐛 Bug Fixes
@@ -534,7 +534,17 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
534
534
  }
535
535
 
536
536
  if (message.type === "direct") {
537
- // If the API provisioned Ably credentials to the instance (reply.agent present),
537
+ // If the API returned agent config and we have an instanceId,
538
+ // provision the config to the instance via SSM (client-side).
539
+ // This runs from the user's infrastructure where AWS permissions exist,
540
+ // rather than from the API server.
541
+ if (reply.agentConfig && message.instanceId) {
542
+ logger.log('Provisioning agent config to instance ' + message.instanceId + ' via SSM...');
543
+ await this._provisionAgentConfig(message.instanceId, reply.agentConfig);
544
+ logger.log('Agent config provisioned successfully.');
545
+ }
546
+
547
+ // If the API returned agent credentials (reply.agent present),
538
548
  // wait for the runner agent to signal readiness before sending commands.
539
549
  // Without this gate, commands published before the agent subscribes are lost.
540
550
  var self = this;
@@ -934,6 +944,51 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
934
944
  this._lastConnectParams = null;
935
945
  this.ps = {};
936
946
  }
947
+
948
+ /**
949
+ * Write the agent config JSON to an EC2 instance via AWS SSM.
950
+ * Runs client-side so the API doesn't need AWS permissions on user infra.
951
+ */
952
+ async _provisionAgentConfig(instanceId, agentConfig) {
953
+ const { execSync } = require('child_process');
954
+ const { writeFileSync, unlinkSync } = require('fs');
955
+ const { join } = require('path');
956
+ const { tmpdir } = require('os');
957
+
958
+ const configJson = JSON.stringify(agentConfig);
959
+ const region = process.env.AWS_REGION || 'us-east-2';
960
+
961
+ // Write SSM parameters to a temp file to avoid shell quoting issues
962
+ const paramsJson = JSON.stringify({
963
+ commands: [
964
+ "$config = '" + configJson.replace(/'/g, "''") + "'",
965
+ "[System.IO.File]::WriteAllText('C:\\Windows\\Temp\\testdriver-agent.json', $config)",
966
+ "Write-Host 'Config written for sandbox " + agentConfig.sandboxId + "'",
967
+ ],
968
+ });
969
+ const tmpFile = join(tmpdir(), 'td-provision-' + Date.now() + '.json');
970
+ writeFileSync(tmpFile, paramsJson);
971
+
972
+ try {
973
+ const output = execSync(
974
+ 'aws ssm send-command --region "' + region + '" --instance-ids "' + instanceId + '" ' +
975
+ '--document-name "AWS-RunPowerShellScript" ' +
976
+ '--parameters file://' + tmpFile + ' --output json',
977
+ { encoding: 'utf-8', timeout: 30000 }
978
+ );
979
+ const cmdId = JSON.parse(output).Command.CommandId;
980
+ logger.log('SSM command sent: ' + cmdId);
981
+
982
+ // Wait for the command to complete
983
+ execSync(
984
+ 'aws ssm wait command-executed --region "' + region + '" ' +
985
+ '--command-id "' + cmdId + '" --instance-id "' + instanceId + '"',
986
+ { encoding: 'utf-8', timeout: 60000 }
987
+ );
988
+ } finally {
989
+ try { unlinkSync(tmpFile); } catch (e) { /* ignore */ }
990
+ }
991
+ }
937
992
  }
938
993
 
939
994
  return new Sandbox();
package/agent/lib/sdk.js CHANGED
@@ -218,6 +218,7 @@ const createSDK = (emitter, config, sessionInstance) => {
218
218
  method: "post",
219
219
  headers: {
220
220
  "Content-Type": "application/json",
221
+ "User-Agent": `TestDriverSDK/${version} (Node.js ${process.version})`,
221
222
  },
222
223
  timeout: 15000, // 15 second timeout for auth requests
223
224
  data: {
@@ -309,6 +310,18 @@ const createSDK = (emitter, config, sessionInstance) => {
309
310
  return rateLimitError;
310
311
  }
311
312
 
313
+ // Forbidden (403) - likely Cloudflare or WAF blocking the request
314
+ if (status === 403) {
315
+ const forbiddenError = new Error(
316
+ "Request blocked (HTTP 403). This may be caused by a firewall or bot protection. " +
317
+ "If this persists, please contact support."
318
+ );
319
+ forbiddenError.code = "REQUEST_BLOCKED";
320
+ forbiddenError.isForbiddenError = true;
321
+ forbiddenError.originalError = error;
322
+ return forbiddenError;
323
+ }
324
+
312
325
  // Other HTTP errors - return with context
313
326
  const url = error.config?.url || apiRoot;
314
327
  const genericError = new Error(
@@ -366,6 +379,7 @@ const createSDK = (emitter, config, sessionInstance) => {
366
379
  method: "post",
367
380
  headers: {
368
381
  "Content-Type": "application/json",
382
+ "User-Agent": `TestDriverSDK/${version} (Node.js ${process.version})`,
369
383
  ...(token && { Authorization: `Bearer ${token}` }),
370
384
  },
371
385
  timeout: 15000,
@@ -430,6 +444,7 @@ const createSDK = (emitter, config, sessionInstance) => {
430
444
  method: "post",
431
445
  headers: {
432
446
  "Content-Type": "application/json",
447
+ "User-Agent": `TestDriverSDK/${version} (Node.js ${process.version})`,
433
448
  ...(token && { Authorization: `Bearer ${token}` }), // Add the authorization bearer token only if token is set
434
449
  ...sentryHeaders, // Add Sentry distributed tracing headers
435
450
  },
@@ -82,9 +82,35 @@ function getExampleFiles() {
82
82
  .sort();
83
83
  }
84
84
 
85
+ // Rewrite internal imports and helpers to their public equivalents
86
+ function transformContentForDocs(content) {
87
+ let transformed = content;
88
+
89
+ // Rewrite internal relative import to the public package path
90
+ transformed = transformed.replace(
91
+ /from\s+["']\.\.\/lib\/vitest\/hooks\.mjs["']/g,
92
+ 'from "testdriverai/vitest/hooks"'
93
+ );
94
+
95
+ // Remove internal config helper import
96
+ transformed = transformed.replace(
97
+ /import\s+\{[^}]*getDefaults[^}]*\}\s+from\s+["']\.\/config\.mjs["'];\n?/g,
98
+ ''
99
+ );
100
+
101
+ // Replace getDefaults(context) spread with inline defaults
102
+ transformed = transformed.replace(
103
+ /\{\s*\.\.\.getDefaults\(context\)\s*/g,
104
+ '{ ip: context.ip || process.env.TD_IP'
105
+ );
106
+
107
+ return transformed;
108
+ }
109
+
85
110
  // Parse test file to extract metadata
86
111
  function parseTestFile(filePath) {
87
- const content = fs.readFileSync(filePath, "utf-8");
112
+ const raw = fs.readFileSync(filePath, "utf-8");
113
+ const content = transformContentForDocs(raw);
88
114
  const filename = path.basename(filePath);
89
115
 
90
116
  // Extract JSDoc comment at top of file
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("AI Test", () => {
33
33
  it("should use ai to search for testdriver on Google", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Assert Test", () => {
33
33
  it("should assert the testdriver login page shows", async (context) => {
@@ -26,7 +26,7 @@ Watch this test execute in a real sandbox environment:
26
26
  * TestDriver SDK - Captcha Test
27
27
  */
28
28
  import { describe, expect, it } from "vitest";
29
- import { TestDriver } from "../lib/vitest/hooks.mjs";
29
+ import { TestDriver } from "testdriverai/vitest/hooks";
30
30
 
31
31
  console.log("DEBUG: process.env.TD_OS:", process.env.TD_OS);
32
32
 
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Chrome Extension Test", () => {
33
33
  it("should load hello-world Chrome extension from local path", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  const isLinux = (process.env.TD_OS || "linux") === "linux";
33
33
 
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Element Not Found Test", () => {
33
33
  it("should handle non-existent element gracefully without timing out", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  /**
33
33
  * Perform login flow for SauceLabs demo app
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Hover Text Test", () => {
33
33
  it("should click Sign In and verify error message", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  const isLinux = (process.env.TD_OS || "linux") === "linux";
33
33
 
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  const isLinux = (process.env.TD_OS || "linux") === "linux";
33
33
 
@@ -29,7 +29,7 @@ Watch this test execute in a real sandbox environment:
29
29
  import path, { dirname } from "path";
30
30
  import { fileURLToPath } from "url";
31
31
  import { describe, expect, it } from "vitest";
32
- import { TestDriver } from "../lib/vitest/hooks.mjs";
32
+ import { TestDriver } from "testdriverai/vitest/hooks";
33
33
 
34
34
  /**
35
35
  * Perform login flow for SauceLabs demo app
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Press Keys Test", () => {
33
33
  it("should create tabs and navigate using keyboard shortcuts", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Scroll Keyboard Test", () => {
33
33
  it("should navigate to webhamster.com and scroll with keyboard", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Scroll Until Image Test", () => {
33
33
  it.skip("should scroll until brown colored house image appears", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  /**
33
33
  * Perform login flow for SauceLabs demo app
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Scroll Test", () => {
33
33
  it("should navigate and scroll down the page", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, expect, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  describe("Type Test", () => {
33
33
  it("should enter standard_user in username field", async (context) => {
@@ -27,7 +27,7 @@ Watch this test execute in a real sandbox environment:
27
27
  */
28
28
 
29
29
  import { describe, it } from "vitest";
30
- import { TestDriver } from "../lib/vitest/hooks.mjs";
30
+ import { TestDriver } from "testdriverai/vitest/hooks";
31
31
 
32
32
  const isLinux = (process.env.TD_OS || "linux") === "linux";
33
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.5.15",
3
+ "version": "7.5.17",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "types": "sdk.d.ts",
File without changes