testdriverai 7.8.0 → 7.9.0-test.1

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.
Files changed (55) hide show
  1. package/agent/index.js +12 -0
  2. package/agent/lib/http.js +21 -3
  3. package/agent/lib/logger.js +15 -0
  4. package/agent/lib/provision-commands.js +176 -0
  5. package/agent/lib/sandbox.js +667 -118
  6. package/agent/lib/sdk.js +1 -20
  7. package/ai/skills/testdriver-find/SKILL.md +14 -20
  8. package/docs/_data/examples-manifest.json +46 -46
  9. package/docs/_scripts/extract-example-urls.js +67 -72
  10. package/docs/docs.json +2 -1
  11. package/docs/v7/examples/ai.mdx +1 -1
  12. package/docs/v7/examples/assert.mdx +1 -1
  13. package/docs/v7/examples/captcha-api.mdx +1 -1
  14. package/docs/v7/examples/chrome-extension.mdx +1 -1
  15. package/docs/v7/examples/drag-and-drop.mdx +1 -1
  16. package/docs/v7/examples/element-not-found.mdx +1 -1
  17. package/docs/v7/examples/exec-output.mdx +1 -1
  18. package/docs/v7/examples/exec-pwsh.mdx +1 -1
  19. package/docs/v7/examples/focus-window.mdx +1 -1
  20. package/docs/v7/examples/hover-image.mdx +1 -1
  21. package/docs/v7/examples/hover-text.mdx +1 -1
  22. package/docs/v7/examples/installer.mdx +1 -1
  23. package/docs/v7/examples/launch-vscode-linux.mdx +1 -1
  24. package/docs/v7/examples/match-image.mdx +1 -1
  25. package/docs/v7/examples/press-keys.mdx +1 -1
  26. package/docs/v7/examples/scroll-keyboard.mdx +1 -1
  27. package/docs/v7/examples/scroll-until-image.mdx +1 -1
  28. package/docs/v7/examples/scroll-until-text.mdx +1 -1
  29. package/docs/v7/examples/scroll.mdx +1 -1
  30. package/docs/v7/examples/type.mdx +1 -1
  31. package/docs/v7/examples/windows-installer.mdx +1 -1
  32. package/docs/v7/find.mdx +14 -20
  33. package/docs/v7/test-results-json.mdx +258 -0
  34. package/examples/scroll-keyboard.test.mjs +1 -1
  35. package/examples/scroll.test.mjs +1 -12
  36. package/interfaces/vitest-plugin.mjs +167 -51
  37. package/lib/core/Dashcam.js +16 -22
  38. package/lib/environments.json +8 -4
  39. package/lib/github-comment.mjs +58 -40
  40. package/lib/init-project.js +5 -67
  41. package/lib/resolve-channel.js +39 -10
  42. package/lib/sentry.js +47 -23
  43. package/lib/vitest/hooks.mjs +117 -20
  44. package/manual/exec-stream-logs.test.mjs +25 -0
  45. package/mcp-server/dist/server.mjs +28 -8
  46. package/mcp-server/src/server.ts +31 -8
  47. package/package.json +2 -1
  48. package/sdk.d.ts +4 -0
  49. package/sdk.js +42 -12
  50. package/setup/aws/install-dev-runner.sh +79 -0
  51. package/setup/aws/spawn-runner.sh +165 -0
  52. package/test-sentry-span.js +35 -0
  53. package/vitest.config.mjs +7 -3
  54. package/vitest.runner.config.mjs +33 -0
  55. package/docs/v7/_drafts/core.mdx +0 -458
package/agent/index.js CHANGED
@@ -70,6 +70,7 @@ class TestDriverAgent extends EventEmitter2 {
70
70
  this.sandboxId = flags["sandbox-id"] || null;
71
71
  this.sandboxAmi = flags["sandbox-ami"] || null;
72
72
  this.sandboxInstance = flags["sandbox-instance"] || null;
73
+ this.e2bTemplateId = flags["e2b-template-id"] || null;
73
74
  this.sandboxOs = flags.os || "linux";
74
75
  this.ip = flags.ip || null;
75
76
  this.workingDir = flags.workingDir || process.cwd();
@@ -211,6 +212,14 @@ class TestDriverAgent extends EventEmitter2 {
211
212
  }
212
213
  }
213
214
 
215
+ // End the Sentry root session span so the trace is finalized
216
+ try {
217
+ const sentry = require("../lib/sentry");
218
+ sentry.clearSessionTraceContext(this.session && this.session.get());
219
+ } catch (e) {
220
+ // Sentry module may not be available, ignore
221
+ }
222
+
214
223
  shouldRunPostrun =
215
224
  !this.hasRunPostrun &&
216
225
  (shouldRunPostrun || this.cliArgs?.command == "run");
@@ -2188,6 +2197,9 @@ Please check your network connection, TD_API_KEY, or the service status.`,
2188
2197
  if (this.sandboxInstance) {
2189
2198
  sandboxConfig.instanceType = this.sandboxInstance;
2190
2199
  }
2200
+ if (this.e2bTemplateId) {
2201
+ sandboxConfig.e2bTemplateId = this.e2bTemplateId;
2202
+ }
2191
2203
  // Add keepAlive TTL if specified
2192
2204
  if (this.keepAlive !== undefined && this.keepAlive !== null) {
2193
2205
  sandboxConfig.keepAlive = this.keepAlive;
package/agent/lib/http.js CHANGED
@@ -16,13 +16,31 @@ const { version } = require("../../package.json");
16
16
  const USER_AGENT = `TestDriverSDK/${version} (Node.js ${process.version})`;
17
17
 
18
18
  /**
19
- * Generate Sentry distributed-tracing headers from a session ID.
20
- * Both sandbox.js and sdk.js duplicated this — it now lives here.
19
+ * Generate Sentry distributed-tracing headers.
20
+ *
21
+ * When Sentry is initialized and a span is active, uses Sentry.getTraceData()
22
+ * so the headers reference the real active span (proper parent-child linkage).
23
+ * Falls back to MD5(sessionId)-based headers when Sentry is not available or
24
+ * has no active span (e.g. TD_TELEMETRY=false).
21
25
  *
22
26
  * @param {string} sessionId
23
- * @returns {object} Headers object (empty if no sessionId)
27
+ * @returns {object} Headers object (empty if no sessionId and no active span)
24
28
  */
25
29
  function getSentryTraceHeaders(sessionId) {
30
+ // Prefer Sentry's own trace propagation when available
31
+ try {
32
+ const Sentry = require("@sentry/node");
33
+ if (typeof Sentry.getTraceData === "function") {
34
+ const traceData = Sentry.getTraceData();
35
+ if (traceData && traceData["sentry-trace"]) {
36
+ return traceData;
37
+ }
38
+ }
39
+ } catch (e) {
40
+ // Sentry not available — fall through to manual derivation
41
+ }
42
+
43
+ // Fallback: derive deterministic trace from session ID
26
44
  if (!sessionId) return {};
27
45
  const traceId = crypto.createHash("md5").update(sessionId).digest("hex");
28
46
  const spanId = crypto.randomBytes(8).toString("hex");
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  const useStderr = process.env.TD_STDIO === 'stderr';
10
+ const isDebug = process.env.TD_DEBUG === 'true' || process.env.VERBOSE === 'true';
10
11
 
11
12
  /**
12
13
  * Log a message - uses stdout by default, stderr if TD_STDIO=stderr
@@ -40,6 +41,19 @@ function warn(...args) {
40
41
  }
41
42
  }
42
43
 
44
+ /**
45
+ * Log a debug message - only outputs when DEBUG=true
46
+ * @param {...any} args - Arguments to log
47
+ */
48
+ function debug(...args) {
49
+ if (!isDebug) return;
50
+ if (useStderr) {
51
+ console.error(...args);
52
+ } else {
53
+ console.log(...args);
54
+ }
55
+ }
56
+
43
57
  /**
44
58
  * Check if logger is configured to use stderr
45
59
  * @returns {boolean}
@@ -50,6 +64,7 @@ function isStderrMode() {
50
64
 
51
65
  module.exports = {
52
66
  log,
67
+ debug,
53
68
  error,
54
69
  warn,
55
70
  isStderrMode,
@@ -0,0 +1,176 @@
1
+ // sdk/agent/lib/provision-commands.js
2
+ // Canonical source of truth for sandbox-agent provisioning commands.
3
+ //
4
+ // These pure functions generate platform-specific command arrays for
5
+ // installing/updating the runner, writing agent config, and starting
6
+ // the sandbox agent. They are used by:
7
+ // - API: _provisionAgentCredentials (Windows SSM)
8
+ // - API: _createLinuxSandbox (E2B bash)
9
+ // - API: direct connection handler (returns commands for SDK to execute)
10
+ // - SDK: _sendSSMCommands (direct connection, client-side SSM)
11
+ //
12
+ // Published as part of the testdriverai npm package.
13
+
14
+ 'use strict';
15
+
16
+ /**
17
+ * Build the agent config object written to the sandbox.
18
+ *
19
+ * @param {Object} opts
20
+ * @param {string} opts.sandboxId
21
+ * @param {string} opts.apiRoot
22
+ * @param {string} [opts.apiKey]
23
+ * @param {string} [opts.sentryDsn]
24
+ * @param {string} [opts.sentryEnvironment]
25
+ * @param {string} [opts.sentryChannel]
26
+ * @param {Object} opts.ablyToken - Ably token object
27
+ * @param {string} opts.channelName - Ably channel name
28
+ * @returns {Object} Agent config to serialize as JSON
29
+ */
30
+ function buildAgentConfig({ sandboxId, apiRoot, apiKey, sentryDsn, sentryEnvironment, sentryChannel, ablyToken, channelName }) {
31
+ return {
32
+ sandboxId,
33
+ apiRoot,
34
+ apiKey: apiKey || undefined,
35
+ sentryDsn: sentryDsn || undefined,
36
+ sentryEnvironment: sentryEnvironment || 'production',
37
+ sentryChannel: sentryChannel || undefined,
38
+ ably: {
39
+ token: ablyToken,
40
+ channel: channelName,
41
+ // Backward compat for old runners (<=7.5.x) that expect multi-channel format
42
+ channels: { commands: channelName, responses: channelName, control: channelName, files: channelName },
43
+ },
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Generate PowerShell commands to provision the sandbox agent on Windows.
49
+ *
50
+ * The returned array is suitable for SSM AWS-RunPowerShellScript Parameters.commands.
51
+ *
52
+ * @param {Object} opts
53
+ * @param {string} opts.channel - Release channel (dev|test|canary|stable)
54
+ * @param {string} opts.configJson - JSON.stringify'd agent config
55
+ * @param {string} opts.sandboxId - For logging
56
+ * @param {string} [opts.s3DownloadUrl] - S3 pre-signed URL for dev/test (omit for npm install)
57
+ * @returns {string[]} Array of PowerShell command strings
58
+ */
59
+ function windowsProvisionCommands({ channel, configJson, sandboxId, s3DownloadUrl }) {
60
+ var useS3 = (channel === 'dev' || channel === 'test') && s3DownloadUrl;
61
+ var commands = [];
62
+
63
+ // ── 1. Stop old runner ────────────────────────────────────────────
64
+ commands.push(
65
+ "Write-Host 'Stopping old runner...'",
66
+ 'Stop-ScheduledTask -TaskName RunTestDriverAgent -ErrorAction SilentlyContinue',
67
+ 'Stop-Process -Name node -Force -ErrorAction SilentlyContinue',
68
+ "Remove-Item 'C:\\Windows\\Temp\\testdriver-agent.json' -Force -ErrorAction SilentlyContinue"
69
+ );
70
+
71
+ // ── 2. Install / update runner ────────────────────────────────────
72
+ commands.push("Set-Location 'C:\\testdriver\\sandbox-agent'");
73
+
74
+ var agentScript;
75
+
76
+ if (useS3) {
77
+ // Dev/test: download tarball from S3, extract, npm install deps
78
+ agentScript = 'sandbox-agent.js';
79
+ commands.push(
80
+ "Write-Host 'Downloading runner from S3 (" + channel + ")...'",
81
+ "$tarball = 'C:\\Windows\\Temp\\runner-dev.tgz'",
82
+ "Invoke-WebRequest -Uri '" + s3DownloadUrl + "' -OutFile $tarball",
83
+ "Write-Host 'Extracting runner...'",
84
+ "tar -xzf $tarball -C 'C:\\Windows\\Temp'",
85
+ "xcopy 'C:\\Windows\\Temp\\package\\*' 'C:\\testdriver\\sandbox-agent\\' /E /Y /I",
86
+ "Remove-Item 'C:\\Windows\\Temp\\package' -Recurse -Force -ErrorAction SilentlyContinue",
87
+ 'Remove-Item $tarball -Force -ErrorAction SilentlyContinue',
88
+ 'npm install --omit=dev 2>&1 | Write-Host',
89
+ "Write-Host 'Runner install complete (s3)'"
90
+ );
91
+ } else {
92
+ // Canary/stable (or dev/test without S3 URL): npm install by dist-tag
93
+ agentScript = 'node_modules/@testdriverai/runner/sandbox-agent.js';
94
+ var runnerTag = channel === 'stable' ? 'latest' : channel;
95
+ commands.push(
96
+ "Write-Host 'Installing @testdriverai/runner@" + runnerTag + "...'",
97
+ 'npm install @testdriverai/runner@' + runnerTag + ' --omit=dev 2>&1 | Write-Host',
98
+ "Write-Host 'Runner install complete'"
99
+ );
100
+ }
101
+
102
+ // ── 3. Regenerate run_testdriver.ps1 ──────────────────────────────
103
+ // Overwrites the baked-in script so the entry point matches the install layout.
104
+ // Uses [IO.File]::WriteAllText to avoid PowerShell variable expansion issues.
105
+ var scriptContent = [
106
+ "Write-Output 'Starting sandbox agent...'",
107
+ "Set-Location 'C:\\testdriver\\sandbox-agent'",
108
+ 'while ($true) {',
109
+ ' & node ' + agentScript + ' 2>&1 | Tee-Object -Append -FilePath C:\\testdriver\\logs\\sandbox-agent.log',
110
+ " Write-Output 'Agent exited, restarting in 2 seconds...'",
111
+ ' Start-Sleep -Seconds 2',
112
+ '}',
113
+ ].join('\r\n');
114
+
115
+ commands.push(
116
+ "Write-Host 'Regenerating run_testdriver.ps1...'",
117
+ "[IO.File]::WriteAllText('C:\\testdriver\\run_testdriver.ps1', '" + scriptContent.replace(/'/g, "''") + "')"
118
+ );
119
+
120
+ // ── 4. Write agent config ─────────────────────────────────────────
121
+ commands.push(
122
+ "Write-Host '=== Writing config ==='",
123
+ "$config = '" + configJson.replace(/'/g, "''") + "'",
124
+ "[System.IO.File]::WriteAllText('C:\\Windows\\Temp\\testdriver-agent.json', $config)",
125
+ "Write-Host 'Config written for sandbox " + sandboxId + "'"
126
+ );
127
+
128
+ // ── 5. Start runner ───────────────────────────────────────────────
129
+ commands.push(
130
+ 'Start-Sleep -Seconds 1',
131
+ 'Start-ScheduledTask -TaskName RunTestDriverAgent',
132
+ "Write-Host 'Runner started'"
133
+ );
134
+
135
+ return commands;
136
+ }
137
+
138
+ /**
139
+ * Generate the bash command to install/update the runner on Linux (E2B).
140
+ *
141
+ * @param {Object} opts
142
+ * @param {string} opts.channel - Release channel
143
+ * @param {string} [opts.s3DownloadUrl] - S3 pre-signed URL for dev/test
144
+ * @param {string} [opts.runnerPath] - Default '/opt/testdriver-runner'
145
+ * @returns {string} Single bash command (steps joined with &&)
146
+ */
147
+ function linuxRunnerInstallCommand({ channel, s3DownloadUrl, runnerPath }) {
148
+ var rp = runnerPath || '/opt/testdriver-runner';
149
+ var useS3 = (channel === 'dev' || channel === 'test') && s3DownloadUrl;
150
+ var runnerTag = channel === 'stable' ? 'latest' : channel;
151
+
152
+ if (useS3) {
153
+ return [
154
+ 'sudo rm -rf ' + rp,
155
+ 'sudo mkdir -p ' + rp,
156
+ 'sudo chown -R user:user ' + rp,
157
+ "curl -sL '" + s3DownloadUrl + "' -o /tmp/runner.tgz",
158
+ 'tar -xzf /tmp/runner.tgz -C /tmp',
159
+ 'cp -r /tmp/package/* ' + rp + '/',
160
+ 'rm -rf /tmp/runner.tgz /tmp/package',
161
+ 'cd ' + rp + ' && npm install --omit=dev --no-audit --no-fund --loglevel=error',
162
+ ].join(' && ');
163
+ }
164
+
165
+ return [
166
+ 'sudo npm install -g @testdriverai/runner@' + runnerTag + ' --omit=dev --no-audit --no-fund --loglevel=error',
167
+ 'sudo rm -rf ' + rp,
168
+ 'sudo ln -sf $(npm root -g)/@testdriverai/runner ' + rp,
169
+ ].join(' && ');
170
+ }
171
+
172
+ module.exports = {
173
+ buildAgentConfig,
174
+ windowsProvisionCommands,
175
+ linuxRunnerInstallCommand,
176
+ };