@testdriverai/agent 7.11.229-canary → 7.11.231-test

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 (100) hide show
  1. package/docs/_data/examples-manifest.json +16 -16
  2. package/docs/_scripts/generate-examples.js +38 -45
  3. package/docs/agent.mdx +174 -93
  4. package/docs/assert.mdx +73 -269
  5. package/docs/aws-setup.mdx +196 -314
  6. package/docs/bring-your-own-machine.mdx +139 -206
  7. package/docs/cache.mdx +144 -151
  8. package/docs/caching.mdx +99 -89
  9. package/docs/captcha.mdx +126 -111
  10. package/docs/ci-cd.mdx +154 -543
  11. package/docs/click.mdx +74 -229
  12. package/docs/client.mdx +177 -231
  13. package/docs/copilot/auto-healing.mdx +86 -370
  14. package/docs/copilot/running-tests.mdx +121 -930
  15. package/docs/customizing-devices.mdx +74 -251
  16. package/docs/dashcam.mdx +72 -405
  17. package/docs/debugging-with-screenshots.mdx +105 -371
  18. package/docs/docs.json +75 -18
  19. package/docs/double-click.mdx +48 -68
  20. package/docs/errors.mdx +102 -176
  21. package/docs/events.mdx +170 -327
  22. package/docs/examples/{hover-text-with-description.mdx → add-to-cart.mdx} +30 -35
  23. package/docs/examples/assert.mdx +28 -41
  24. package/docs/examples/click-button.mdx +71 -0
  25. package/docs/examples/{hover-image.mdx → click-icon.mdx} +32 -28
  26. package/docs/examples/element-not-found.mdx +27 -26
  27. package/docs/examples/findall-coffee-icons.mdx +28 -32
  28. package/docs/examples/installer.mdx +61 -25
  29. package/docs/examples/launch-vscode-linux.mdx +28 -51
  30. package/docs/examples/navigate-and-scroll.mdx +82 -0
  31. package/docs/examples/parse.mdx +24 -28
  32. package/docs/examples/press-keys.mdx +28 -25
  33. package/docs/examples/scroll.mdx +25 -27
  34. package/docs/examples/type.mdx +26 -21
  35. package/docs/exec.mdx +92 -256
  36. package/docs/extract.mdx +69 -170
  37. package/docs/find.mdx +140 -736
  38. package/docs/focus-application.mdx +69 -246
  39. package/docs/generating-tests.mdx +103 -137
  40. package/docs/guide/best-practices-polling.mdx +65 -141
  41. package/docs/hosted.mdx +80 -144
  42. package/docs/hover.mdx +62 -235
  43. package/docs/locating-elements.mdx +74 -369
  44. package/docs/making-assertions.mdx +116 -74
  45. package/docs/mcp-setup.mdx +80 -63
  46. package/docs/mouse-down.mdx +48 -125
  47. package/docs/mouse-up.mdx +47 -129
  48. package/docs/options.mdx +75 -48
  49. package/docs/parse.mdx +95 -184
  50. package/docs/performing-actions.mdx +104 -94
  51. package/docs/press-keys.mdx +59 -299
  52. package/docs/provision.mdx +137 -182
  53. package/docs/quickstart-claude.mdx +85 -0
  54. package/docs/quickstart-github.mdx +62 -32
  55. package/docs/quickstart-manual.mdx +60 -33
  56. package/docs/quickstart-vscode.mdx +97 -0
  57. package/docs/quickstart.mdx +25 -9
  58. package/docs/redraw.mdx +101 -165
  59. package/docs/reusable-code.mdx +97 -200
  60. package/docs/right-click.mdx +47 -89
  61. package/docs/screenshot.mdx +47 -201
  62. package/docs/screenshots.mdx +87 -137
  63. package/docs/scroll.mdx +71 -154
  64. package/docs/secrets.mdx +96 -91
  65. package/docs/self-hosted.mdx +83 -142
  66. package/docs/snippets/enterprise.mdx +15 -0
  67. package/docs/styles.css +68 -0
  68. package/docs/test-results-json.mdx +167 -204
  69. package/docs/type.mdx +56 -304
  70. package/docs/variables.mdx +121 -81
  71. package/docs/wait.mdx +33 -31
  72. package/docs/what-is-testdriver.mdx +80 -32
  73. package/examples/{hover-text-with-description.test.mjs → add-to-cart.test.mjs} +4 -5
  74. package/examples/assert.test.mjs +3 -20
  75. package/examples/{hover-text.test.mjs → click-button.test.mjs} +3 -4
  76. package/examples/{hover-image.test.mjs → click-icon.test.mjs} +5 -6
  77. package/examples/element-not-found.test.mjs +3 -5
  78. package/examples/findall-coffee-icons.test.mjs +3 -3
  79. package/examples/installer.test.mjs +1 -1
  80. package/examples/launch-vscode-linux.test.mjs +6 -33
  81. package/examples/{scroll-keyboard.test.mjs → navigate-and-scroll.test.mjs} +4 -5
  82. package/examples/parse.test.mjs +1 -2
  83. package/examples/press-keys.test.mjs +4 -4
  84. package/examples/scroll.test.mjs +3 -6
  85. package/examples/type.test.mjs +5 -2
  86. package/lib/provision.js +6 -2
  87. package/package.json +1 -1
  88. package/sdk.d.ts +4 -2
  89. package/sdk.js +1 -1
  90. package/docs/claude-mcp-plugin.mdx +0 -244
  91. package/docs/elements.mdx +0 -606
  92. package/docs/examples/ai.mdx +0 -78
  93. package/docs/examples/captcha-api.mdx +0 -92
  94. package/docs/examples/chrome-extension.mdx +0 -156
  95. package/docs/examples/drag-and-drop.mdx +0 -100
  96. package/docs/examples/formatted-logging.mdx +0 -74
  97. package/docs/examples/hover-text.mdx +0 -70
  98. package/docs/examples/match-image.mdx +0 -97
  99. package/docs/examples/scroll-keyboard.mdx +0 -80
  100. package/docs/examples/scroll-until-text.mdx +0 -109
package/docs/client.mdx CHANGED
@@ -1,323 +1,269 @@
1
1
  ---
2
- title: "Client & Connection"
3
- sidebarTitle: "Client & Connection"
4
- description: "Create the TestDriver client, authenticate, and connect to a sandbox"
2
+ title: "Client & connection"
3
+ sidebarTitle: "Client & connection"
4
+ description: "Create a TestDriver client, connect it to a sandbox, and disconnect when you're done."
5
5
  icon: "plug"
6
6
  ---
7
7
 
8
- ## Overview
8
+ import { EnterpriseBadge, EnterpriseCta } from '/snippets/enterprise.mdx';
9
9
 
10
- The `TestDriver` client is the main entry point for the SDK. It does the authentication and the sandbox connection. It gives access to all test methods.
10
+ Every test talks to its sandbox through a TestDriver client. There are two ways to create one:
11
11
 
12
- ## Constructor
12
+ - **`TestDriver(context)`** from `testdriverai/vitest/hooks`. Use this in Vitest tests. It connects and disconnects for you.
13
+ - **`new TestDriver()`** from `testdriverai`. Use this outside Vitest's per-test hooks, when you manage the connection yourself.
13
14
 
14
15
  ```javascript
15
- const testdriver = new TestDriver(apiKey, options)
16
+ import { TestDriver } from 'testdriverai/vitest/hooks';
17
+
18
+ test('opens the login page', async (context) => {
19
+ const testdriver = TestDriver(context);
20
+ await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
21
+ });
22
+ ```
23
+
24
+ ## TestDriver(context, options)
25
+
26
+ Creates a client for the current Vitest test.
27
+
28
+ ```typescript
29
+ TestDriver(context: TestContext, options?: TestDriverOptions): TestDriver
16
30
  ```
17
31
 
18
32
  ### Parameters
19
33
 
20
- <ParamField path="apiKey" type="string" required>
21
- Your TestDriver API key from the [dashboard](https://console.testdriver.ai/settings)
34
+ <ParamField path="context" type="TestContext" required>
35
+ The context Vitest passes to your test function, as in `async (context) => { ... }`. TestDriver throws if it's missing.
22
36
  </ParamField>
23
37
 
24
- <ParamField path="options" type="object">
25
- The configuration options for the client. See [SDK Options](/options) for the full list, with defaults and examples for each option.
38
+ <ParamField path="options" type="TestDriverOptions">
39
+ Client options, such as `os`, `preview`, `cache`, or `keepAlive`. See [Options](/options) for the full list.
40
+
41
+ <Expandable title="hook-only options">
42
+ <ParamField path="apiKey" type="string">
43
+ Your TestDriver API key. Defaults to the `TD_API_KEY` environment variable, which TestDriver also loads from `.env`.
44
+ </ParamField>
45
+ <ParamField path="debugOnFailure" type="boolean" default="false">
46
+ If the test fails, keep its sandbox running instead of disconnecting, and print the sandbox id so you can reconnect to it.
47
+ </ParamField>
48
+ </Expandable>
26
49
  </ParamField>
27
50
 
28
- ### Example
51
+ ### What it does
29
52
 
30
- ```javascript
31
- import TestDriver from 'testdriverai';
53
+ - **Connects automatically.** It authenticates and connects to a sandbox in the background. Your first command waits for the connection.
54
+ - **Disconnects automatically** when the test finishes.
55
+ - **Returns one client per test.** Calling it again in the same test returns the same client. A retried test gets a new one.
56
+ - **Reads `TD_OS`.** If you don't pass `os`, it uses the `TD_OS` environment variable, then Linux.
32
57
 
33
- // API key is automatically loaded from TD_API_KEY in .env
34
- const testdriver = new TestDriver({
35
- os: 'windows',
36
- resolution: '1920x1080',
37
- logging: true,
38
- analytics: true
39
- });
58
+ ### Returns
40
59
 
41
- // With AI config for stricter verification
42
- const testdriver = new TestDriver({
43
- ai: { temperature: 0, top: { p: 0.9, k: 40 } }
44
- });
60
+ A connected `TestDriver` client.
45
61
 
46
- // Or pass API key explicitly
47
- const testdriver = new TestDriver('your-api-key', {
48
- os: 'windows'
49
- });
62
+ ## new TestDriver(apiKey, options)
63
+
64
+ Creates a client that you connect and disconnect yourself.
65
+
66
+ ```typescript
67
+ new TestDriver(apiKey?: string, options?: TestDriverOptions)
68
+ new TestDriver(options?: TestDriverOptions)
50
69
  ```
51
70
 
52
- ## Authentication
71
+ ### Parameters
53
72
 
54
- ### auth()
73
+ <ParamField path="apiKey" type="string">
74
+ Your TestDriver API key. Defaults to `TD_API_KEY`, which TestDriver loads from `.env`. Create one at [console.testdriver.ai/team/api-keys](https://console.testdriver.ai/team/api-keys).
75
+ </ParamField>
55
76
 
56
- Authenticate with the TestDriver API.
77
+ <ParamField path="options" type="TestDriverOptions">
78
+ Client options. See [Options](/options).
79
+ </ParamField>
57
80
 
58
- ```javascript
59
- await testdriver.auth()
60
- ```
81
+ ### Example
61
82
 
62
- **Returns:** `Promise<string>` - Authentication token
83
+ Share one sandbox across a test file by connecting in `beforeAll`:
63
84
 
64
- **Example:**
65
- ```javascript
66
- await testdriver.auth();
67
- ```
85
+ ```javascript tests/dashboard.test.mjs
86
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
87
+ import TestDriver from 'testdriverai';
68
88
 
69
- <Note>
70
- You must call `auth()` before `connect()`. Most examples call both sequentially.
71
- </Note>
89
+ describe('Dashboard', () => {
90
+ let testdriver;
72
91
 
73
- ## Connection Management
92
+ beforeAll(async () => {
93
+ testdriver = new TestDriver({ os: 'linux' });
94
+ await testdriver.connect();
95
+ await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
96
+ });
74
97
 
75
- ### connect()
98
+ afterAll(async () => {
99
+ await testdriver.disconnect();
100
+ });
76
101
 
77
- Connect to a sandbox environment. This creates or reconnects to a virtual machine where your tests will run.
102
+ it('shows the login form', async () => {
103
+ const result = await testdriver.assert('a login form is visible');
104
+ expect(result).toBe(true);
105
+ });
106
+ });
107
+ ```
78
108
 
79
- ```javascript
80
- await testdriver.connect(options)
109
+ ## connect(options)
110
+
111
+ Connects the client to a sandbox. By default, it creates a new sandbox.
112
+
113
+ ```typescript
114
+ testdriver.connect(options?: ConnectOptions): Promise<SandboxInstance>
81
115
  ```
82
116
 
83
- #### Parameters
117
+ `connect()` authenticates first if needed. You don't need to call `auth()` yourself.
84
118
 
85
- <ParamField path="options" type="object">
86
- Connection options
87
-
119
+ ### Parameters
120
+
121
+ <ParamField path="options" type="ConnectOptions">
88
122
  <Expandable title="properties">
89
- <ParamField path="newSandbox" type="boolean" default="false">
90
- Force creation of a new sandbox instead of reusing an existing one
91
- </ParamField>
92
-
93
123
  <ParamField path="sandboxId" type="string">
94
- Existing sandbox ID to reconnect to
124
+ Reconnect to this existing sandbox instead of creating one.
125
+ </ParamField>
126
+ <ParamField path="requireSandbox" type="boolean" default="false">
127
+ With `sandboxId`, throw if that sandbox is gone, instead of creating a new, empty one in its place.
128
+ </ParamField>
129
+ <ParamField path="reconnect" type="boolean">
130
+ Reconnect to the last sandbox this project used, recorded in `.testdriver/last-sandbox`. Provision calls are skipped, because the app is already running. Throws if there's no previous sandbox.
131
+ </ParamField>
132
+ <ParamField path="newSandbox" type="boolean" default="true">
133
+ Create a new sandbox. Set to `false` when you pass `sandboxId` or `reconnect`.
134
+ </ParamField>
135
+ <ParamField path="keepAlive" type="number" default="60000">
136
+ Milliseconds to keep the sandbox running after `disconnect()`, so you can reconnect. Capped at 30 minutes. `0` uses the default.
137
+ </ParamField>
138
+ <ParamField path="os" type="string" default="linux">
139
+ `"linux"` or `"windows"`. <EnterpriseBadge /> Windows is available on Enterprise plans.
140
+ </ParamField>
141
+ <ParamField path="preview" type="string" default="browser">
142
+ Where to show the live run: `"browser"`, `"ide"` (needs the TestDriver extension), or `"none"`.
143
+ </ParamField>
144
+ <ParamField path="reuseConnection" type="boolean" default="true">
145
+ Reuse a recent connection if one is available.
95
146
  </ParamField>
96
-
97
147
  <ParamField path="ip" type="string">
98
- Direct IP address to connect to (for self-hosted sandboxes)
148
+ Connect directly to a running sandbox at this IP address, for self-hosted machines. <EnterpriseBadge />
99
149
  </ParamField>
100
-
101
150
  <ParamField path="sandboxAmi" type="string">
102
- AMI to use for the sandbox (AWS deployments)
151
+ Custom AMI ID for the sandbox, for self-hosted AWS deployments. <EnterpriseBadge />
103
152
  </ParamField>
104
-
105
153
  <ParamField path="sandboxInstance" type="string">
106
- Instance type for the sandbox (AWS deployments)
154
+ EC2 instance type for the sandbox, for self-hosted AWS deployments. <EnterpriseBadge />
107
155
  </ParamField>
108
-
109
- <ParamField path="preview" type="string" default="browser">
110
- Preview mode for live test visualization:
111
- - `"browser"` - Opens debugger in default browser (default)
112
- - `"ide"` - Opens preview in IDE panel (VSCode, Cursor - requires TestDriver extension)
113
- - `"none"` - Headless mode, no visual preview
156
+ <ParamField path="e2bTemplateId" type="string">
157
+ E2B template ID to create the sandbox from.
114
158
  </ParamField>
115
-
116
159
  <ParamField path="headless" type="boolean" default="false">
117
- **Deprecated**: Use `preview: "none"` instead. Run in headless mode without opening the debugger.
118
- </ParamField>
119
-
120
- <ParamField path="keepAlive" type="number" default="60000">
121
- Keep sandbox alive for the specified number of milliseconds after disconnect. Set to `0` to terminate immediately on disconnect. Useful for debugging or reconnecting to the same sandbox.
160
+ Deprecated. Use `preview: "none"` instead.
122
161
  </ParamField>
123
162
  </Expandable>
124
163
  </ParamField>
125
164
 
126
- **Returns:** `Promise&lt;Object&gt;` - Sandbox instance details including `instanceId`, `ip`, `vncPort`, etc.
165
+ ### Returns
127
166
 
128
- #### Examples
129
-
130
- **Basic connection:**
131
- ```javascript
132
- await testdriver.connect();
133
- ```
134
-
135
- **Reconnect to existing sandbox:**
136
- ```javascript
137
- const instance = await testdriver.connect({
138
- sandboxId: 'existing-sandbox-id-123'
139
- });
140
- ```
167
+ <ResponseField name="SandboxInstance" type="object">
168
+ <Expandable title="properties">
169
+ <ResponseField name="instanceId" type="string">
170
+ The sandbox's ID.
171
+ </ResponseField>
172
+ <ResponseField name="ip" type="string">
173
+ The sandbox's IP address.
174
+ </ResponseField>
175
+ <ResponseField name="vncPort" type="number">
176
+ The port for the sandbox's remote display.
177
+ </ResponseField>
178
+ </Expandable>
179
+ </ResponseField>
141
180
 
142
- **Self-hosted sandbox:**
143
- ```javascript
144
- await testdriver.connect({
145
- ip: '192.168.1.100'
146
- });
147
- ```
181
+ ### Errors
148
182
 
149
- ### disconnect()
183
+ Throws `Already connected` if the client is already connected. Create a new client to connect again.
150
184
 
151
- Disconnect from the sandbox and clean up resources.
185
+ ### Example
152
186
 
153
187
  ```javascript
154
- await testdriver.disconnect()
188
+ const testdriver = new TestDriver();
189
+
190
+ // Reattach to a sandbox you kept alive earlier
191
+ const sandbox = await testdriver.connect({ sandboxId: 'sandbox-abc123', newSandbox: false });
192
+ console.log(sandbox.instanceId);
155
193
  ```
156
194
 
157
- **Returns:** `Promise<void>`
195
+ ## disconnect()
158
196
 
159
- **Example:**
160
- ```javascript
161
- afterAll(async () => {
162
- await testdriver.disconnect();
163
- });
197
+ Disconnects from the sandbox. The sandbox keeps running for the `keepAlive` time, then shuts down.
198
+
199
+ ```typescript
200
+ testdriver.disconnect(): Promise<void>
164
201
  ```
165
202
 
166
- ## Instance Information
203
+ The Vitest hook calls this for you.
167
204
 
168
- ### getInstance()
205
+ ## auth()
169
206
 
170
- Get the current sandbox instance details.
207
+ Authenticates with the TestDriver API and resolves to a session token.
171
208
 
172
- ```javascript
173
- const instance = testdriver.getInstance()
209
+ ```typescript
210
+ testdriver.auth(): Promise<string>
174
211
  ```
175
212
 
176
- **Returns:** `Object | null` - Sandbox instance information
213
+ You rarely need it, because `connect()` calls it when needed.
177
214
 
178
- **Example:**
179
- ```javascript
180
- const instance = testdriver.getInstance();
181
- console.log('Instance ID:', instance.instanceId);
182
- console.log('IP Address:', instance.ip);
183
- ```
184
-
185
- ### getSessionId()
215
+ ## getInstance()
186
216
 
187
- Get the current session ID for tracking and debugging.
217
+ Returns the connected sandbox's details, or `null` if the client isn't connected.
188
218
 
189
- ```javascript
190
- const sessionId = testdriver.getSessionId()
219
+ ```typescript
220
+ testdriver.getInstance(): SandboxInstance | null
191
221
  ```
192
222
 
193
- **Returns:** `string | null` - Session ID
223
+ ## getSessionId()
194
224
 
195
- **Example:**
196
- ```javascript
197
- const sessionId = testdriver.getSessionId();
198
- console.log('Session:', sessionId);
199
- ```
225
+ Returns the current session ID, or `null`. Include it when you report a problem.
200
226
 
201
- ## Logging & Events
227
+ ```typescript
228
+ testdriver.getSessionId(): string | null
229
+ ```
202
230
 
203
- ### setLogging()
231
+ ## getLastSandboxId()
204
232
 
205
- Enable or disable console logging at runtime.
233
+ Returns the most recent sandbox, or `null` if there isn't one. It reads from the connected client, or from `.testdriver/last-sandbox`.
206
234
 
207
- ```javascript
208
- testdriver.setLogging(enabled)
235
+ ```typescript
236
+ testdriver.getLastSandboxId(): { sandboxId: string; os?: 'windows' | 'linux'; createdAt?: number } | null
209
237
  ```
210
238
 
211
- **Parameters:**
212
- - `enabled` (boolean) - Whether to enable logging
239
+ ## setLogging(enabled)
213
240
 
214
- **Example:**
215
- ```javascript
216
- // Disable logging for cleanup operations
217
- testdriver.setLogging(false);
218
- await testdriver.disconnect();
219
- testdriver.setLogging(true);
241
+ Turns console logging on or off while the test runs.
242
+
243
+ ```typescript
244
+ testdriver.setLogging(enabled: boolean): void
220
245
  ```
221
246
 
222
- ### getEmitter()
247
+ ## getEmitter()
223
248
 
224
- Get the event emitter for custom event handling.
249
+ Returns the client's event emitter, so you can react to what TestDriver does.
225
250
 
226
- ```javascript
227
- const emitter = testdriver.getEmitter()
251
+ ```typescript
252
+ testdriver.getEmitter(): EventEmitter2
228
253
  ```
229
254
 
230
- **Returns:** `EventEmitter2` - Event emitter instance
231
-
232
- **Example:**
233
255
  ```javascript
234
256
  const emitter = testdriver.getEmitter();
235
257
 
236
- emitter.on('command:start', (data) => {
237
- console.log('Command started:', data);
238
- });
239
-
240
- emitter.on('command:success', (data) => {
241
- console.log('Command succeeded:', data);
242
- });
243
-
244
- emitter.on('command:error', (error) => {
245
- console.error('Command failed:', error);
246
- });
258
+ emitter.on('log:warn', (message) => console.warn('TestDriver warning:', message));
259
+ emitter.on('error:sdk', (error) => console.error('TestDriver API error:', error.message));
247
260
  ```
248
261
 
249
- ## Complete Example
250
-
251
- ```javascript
252
- import { beforeAll, afterAll, describe, it } from 'vitest';
253
- import TestDriver from 'testdriverai';
254
-
255
- describe('My Test Suite', () => {
256
- let testdriver;
262
+ See [Events](/events) for every event name.
257
263
 
258
- beforeAll(async () => {
259
- // Initialize client - API key loaded automatically from .env
260
- testdriver = new TestDriver({
261
- os: 'windows',
262
- resolution: '1366x768',
263
- logging: true
264
- });
265
-
266
- // Set up event listeners
267
- const emitter = testdriver.getEmitter();
268
- emitter.on('log:info', (msg) => console.log('[INFO]', msg));
269
-
270
- // Authenticate and connect
271
- await testdriver.auth();
272
- const instance = await testdriver.connect();
273
-
274
- console.log('Connected to sandbox:', instance.instanceId);
275
- });
276
-
277
- afterAll(async () => {
278
- await testdriver.disconnect();
279
- });
280
-
281
- it('runs a test', async () => {
282
- // Your test code here
283
- });
284
- });
285
- ```
264
+ ## Related
286
265
 
287
- ## Best Practices
288
-
289
- <AccordionGroup>
290
- <Accordion title="Reuse sandboxes across tests">
291
- Use `beforeAll`/`afterAll` to create one sandbox per test suite rather than per test. This significantly reduces execution time.
292
- </Accordion>
293
-
294
- <Accordion title="Handle connection errors gracefully">
295
- Wrap `connect()` in a try-catch block to handle network issues or quota limits:
296
-
297
- ```javascript
298
- try {
299
- await testdriver.connect();
300
- } catch (error) {
301
- console.error('Failed to connect:', error.message);
302
- throw error;
303
- }
304
- ```
305
- </Accordion>
306
-
307
- <Accordion title="Always disconnect">
308
- Use `afterAll` or try-finally blocks to ensure `disconnect()` is called even if tests fail. This prevents orphaned sandboxes.
309
- </Accordion>
310
-
311
- <Accordion title="Use environment variables for API keys">
312
- Never hardcode API keys. The SDK automatically loads `TD_API_KEY` from your `.env` file:
313
-
314
- ```bash .env
315
- TD_API_KEY=your_api_key_here
316
- ```
317
-
318
- ```javascript
319
- // API key is loaded automatically - no need to pass it!
320
- const testdriver = new TestDriver();
321
- ```
322
- </Accordion>
323
- </AccordionGroup>
266
+ - [Options](/options): every option you can pass to the client
267
+ - [Launch apps](/provision): start Chrome, extensions, and desktop apps once connected
268
+ - [Run tests](/copilot/running-tests#reuse-a-sandbox-while-you-iterate): reuse a sandbox between runs
269
+ - [Events](/events): listen for commands, logs, and errors