testdriverai 7.8.0-test.9 → 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.
- package/agent/index.js +12 -0
- package/agent/lib/http.js +21 -3
- package/agent/lib/logger.js +15 -0
- package/agent/lib/provision-commands.js +176 -0
- package/agent/lib/sandbox.js +667 -118
- package/agent/lib/sdk.js +1 -20
- package/ai/skills/testdriver-find/SKILL.md +14 -20
- package/docs/_data/examples-manifest.json +46 -46
- package/docs/_scripts/extract-example-urls.js +67 -72
- package/docs/changelog.mdx +26 -0
- package/docs/docs.json +2 -1
- package/docs/v7/examples/ai.mdx +1 -1
- package/docs/v7/examples/assert.mdx +1 -1
- package/docs/v7/examples/captcha-api.mdx +1 -1
- package/docs/v7/examples/chrome-extension.mdx +1 -1
- package/docs/v7/examples/drag-and-drop.mdx +1 -1
- package/docs/v7/examples/element-not-found.mdx +1 -1
- package/docs/v7/examples/exec-output.mdx +1 -1
- package/docs/v7/examples/exec-pwsh.mdx +1 -1
- package/docs/v7/examples/focus-window.mdx +1 -1
- package/docs/v7/examples/hover-image.mdx +1 -1
- package/docs/v7/examples/hover-text.mdx +1 -1
- package/docs/v7/examples/installer.mdx +1 -1
- package/docs/v7/examples/launch-vscode-linux.mdx +1 -1
- package/docs/v7/examples/match-image.mdx +1 -1
- package/docs/v7/examples/press-keys.mdx +1 -1
- package/docs/v7/examples/scroll-keyboard.mdx +1 -1
- package/docs/v7/examples/scroll-until-image.mdx +1 -1
- package/docs/v7/examples/scroll-until-text.mdx +1 -1
- package/docs/v7/examples/scroll.mdx +1 -1
- package/docs/v7/examples/type.mdx +1 -1
- package/docs/v7/examples/windows-installer.mdx +1 -1
- package/docs/v7/find.mdx +14 -20
- package/docs/v7/test-results-json.mdx +258 -0
- package/examples/scroll-keyboard.test.mjs +1 -1
- package/examples/scroll.test.mjs +1 -12
- package/interfaces/vitest-plugin.mjs +167 -51
- package/lib/core/Dashcam.js +18 -31
- package/lib/environments.json +8 -4
- package/lib/github-comment.mjs +58 -40
- package/lib/init-project.js +5 -67
- package/lib/resolve-channel.js +39 -10
- package/lib/sentry.js +47 -23
- package/lib/vitest/hooks.mjs +117 -20
- package/manual/exec-stream-logs.test.mjs +25 -0
- package/mcp-server/dist/server.mjs +28 -8
- package/mcp-server/src/server.ts +31 -8
- package/package.json +2 -1
- package/sdk.d.ts +4 -0
- package/sdk.js +42 -12
- package/setup/aws/install-dev-runner.sh +79 -0
- package/setup/aws/spawn-runner.sh +165 -0
- package/test-sentry-span.js +35 -0
- package/vitest.config.mjs +7 -3
- package/vitest.runner.config.mjs +33 -0
- 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
|
|
20
|
-
*
|
|
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");
|
package/agent/lib/logger.js
CHANGED
|
@@ -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
|
+
};
|