@testdriverai/agent 7.8.0-canary.10

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 (528) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.env.example +4 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc +1 -0
  5. package/CHANGELOG.md +953 -0
  6. package/README.md +81 -0
  7. package/agent/events.js +135 -0
  8. package/agent/index.js +2450 -0
  9. package/agent/interface.js +35 -0
  10. package/agent/lib/analytics.js +22 -0
  11. package/agent/lib/censorship.js +75 -0
  12. package/agent/lib/commander.js +246 -0
  13. package/agent/lib/commands.js +1684 -0
  14. package/agent/lib/config.js +60 -0
  15. package/agent/lib/generator.js +91 -0
  16. package/agent/lib/http.js +144 -0
  17. package/agent/lib/logger.js +56 -0
  18. package/agent/lib/outputs.js +29 -0
  19. package/agent/lib/parser.js +209 -0
  20. package/agent/lib/redraw.js +386 -0
  21. package/agent/lib/resources/cursor-2.png +0 -0
  22. package/agent/lib/sandbox.js +1104 -0
  23. package/agent/lib/sdk.js +633 -0
  24. package/agent/lib/session.js +25 -0
  25. package/agent/lib/source-mapper.js +342 -0
  26. package/agent/lib/subimage/index.js +77 -0
  27. package/agent/lib/subimage/opencv.js +69 -0
  28. package/agent/lib/system.js +204 -0
  29. package/agent/lib/theme.js +14 -0
  30. package/agent/lib/valid-version.js +21 -0
  31. package/agent/lib/validation.js +169 -0
  32. package/ai/.claude-plugin/plugin.json +9 -0
  33. package/ai/agents/testdriver.md +638 -0
  34. package/ai/skills/testdriver-ai/SKILL.md +204 -0
  35. package/ai/skills/testdriver-assert/SKILL.md +315 -0
  36. package/ai/skills/testdriver-aws-setup/SKILL.md +448 -0
  37. package/ai/skills/testdriver-cache/SKILL.md +221 -0
  38. package/ai/skills/testdriver-caching/SKILL.md +124 -0
  39. package/ai/skills/testdriver-captcha/SKILL.md +158 -0
  40. package/ai/skills/testdriver-ci-cd/SKILL.md +602 -0
  41. package/ai/skills/testdriver-click/SKILL.md +286 -0
  42. package/ai/skills/testdriver-client/SKILL.md +477 -0
  43. package/ai/skills/testdriver-cloud/SKILL.md +119 -0
  44. package/ai/skills/testdriver-customizing-devices/SKILL.md +319 -0
  45. package/ai/skills/testdriver-dashcam/SKILL.md +418 -0
  46. package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +401 -0
  47. package/ai/skills/testdriver-device-config/SKILL.md +317 -0
  48. package/ai/skills/testdriver-double-click/SKILL.md +102 -0
  49. package/ai/skills/testdriver-elements/SKILL.md +605 -0
  50. package/ai/skills/testdriver-enterprise/SKILL.md +114 -0
  51. package/ai/skills/testdriver-errors/SKILL.md +246 -0
  52. package/ai/skills/testdriver-events/SKILL.md +356 -0
  53. package/ai/skills/testdriver-examples/SKILL.md +7 -0
  54. package/ai/skills/testdriver-exec/SKILL.md +317 -0
  55. package/ai/skills/testdriver-find/SKILL.md +829 -0
  56. package/ai/skills/testdriver-focus-application/SKILL.md +293 -0
  57. package/ai/skills/testdriver-generating-tests/SKILL.md +36 -0
  58. package/ai/skills/testdriver-hover/SKILL.md +278 -0
  59. package/ai/skills/testdriver-locating-elements/SKILL.md +71 -0
  60. package/ai/skills/testdriver-making-assertions/SKILL.md +32 -0
  61. package/ai/skills/testdriver-mcp/SKILL.md +7 -0
  62. package/ai/skills/testdriver-mcp-workflow/SKILL.md +410 -0
  63. package/ai/skills/testdriver-mouse-down/SKILL.md +161 -0
  64. package/ai/skills/testdriver-mouse-up/SKILL.md +164 -0
  65. package/ai/skills/testdriver-parse/SKILL.md +236 -0
  66. package/ai/skills/testdriver-performing-actions/SKILL.md +54 -0
  67. package/ai/skills/testdriver-press-keys/SKILL.md +348 -0
  68. package/ai/skills/testdriver-provision/SKILL.md +331 -0
  69. package/ai/skills/testdriver-quickstart/SKILL.md +144 -0
  70. package/ai/skills/testdriver-redraw/SKILL.md +214 -0
  71. package/ai/skills/testdriver-reusable-code/SKILL.md +249 -0
  72. package/ai/skills/testdriver-right-click/SKILL.md +123 -0
  73. package/ai/skills/testdriver-running-tests/SKILL.md +185 -0
  74. package/ai/skills/testdriver-screenshot/SKILL.md +248 -0
  75. package/ai/skills/testdriver-screenshots/SKILL.md +184 -0
  76. package/ai/skills/testdriver-scroll/SKILL.md +335 -0
  77. package/ai/skills/testdriver-secrets/SKILL.md +115 -0
  78. package/ai/skills/testdriver-self-hosted/SKILL.md +65 -0
  79. package/ai/skills/testdriver-test-writer/SKILL.md +448 -0
  80. package/ai/skills/testdriver-testdriver/SKILL.md +628 -0
  81. package/ai/skills/testdriver-testdriver-mechanic/SKILL.md +165 -0
  82. package/ai/skills/testdriver-type/SKILL.md +357 -0
  83. package/ai/skills/testdriver-variables/SKILL.md +111 -0
  84. package/ai/skills/testdriver-wait/SKILL.md +50 -0
  85. package/ai/skills/testdriver-waiting-for-elements/SKILL.md +90 -0
  86. package/ai/skills/testdriver-what-is-testdriver/SKILL.md +54 -0
  87. package/bin/testdriverai.js +22 -0
  88. package/debugger/bg.png +0 -0
  89. package/debugger/icon.png +0 -0
  90. package/debugger/index.html +469 -0
  91. package/debugger/td.png +0 -0
  92. package/debugger/tray-buffered.png +0 -0
  93. package/debugger/tray.png +0 -0
  94. package/docs/GITHUB_COMMENTS.md +330 -0
  95. package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +167 -0
  96. package/docs/QUICK-START-GITHUB-COMMENTS.md +84 -0
  97. package/docs/TEST-GITHUB-COMMENTS.md +129 -0
  98. package/docs/_data/examples-manifest.json +177 -0
  99. package/docs/_data/examples-manifest.schema.json +41 -0
  100. package/docs/_scripts/extract-example-urls.js +165 -0
  101. package/docs/_scripts/generate-examples.js +560 -0
  102. package/docs/_scripts/generate-skills.js +154 -0
  103. package/docs/_scripts/link-replacer.js +164 -0
  104. package/docs/_scripts/upload-docs-to-openai.js +284 -0
  105. package/docs/changelog.mdx +161 -0
  106. package/docs/claude-mcp-plugin.mdx +160 -0
  107. package/docs/docs.json +442 -0
  108. package/docs/github-integration-setup.md +266 -0
  109. package/docs/guide/best-practices-polling.mdx +174 -0
  110. package/docs/images/content/account/newprojectsettings.png +0 -0
  111. package/docs/images/content/account/projectpage.png +0 -0
  112. package/docs/images/content/account/projectreplays.png +0 -0
  113. package/docs/images/content/account/team-manage.png +0 -0
  114. package/docs/images/content/account/teampage.png +0 -0
  115. package/docs/images/content/extension/cursor.svg +1 -0
  116. package/docs/images/content/extension/vscode.svg +57 -0
  117. package/docs/images/content/extension/windsurf.svg +3 -0
  118. package/docs/images/content/parse/output.png +0 -0
  119. package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
  120. package/docs/images/content/side-by-side.png +0 -0
  121. package/docs/images/content/vscode/ide-full.png +0 -0
  122. package/docs/images/content/vscode/running.png +0 -0
  123. package/docs/images/content/vscode/v7-chat.png +0 -0
  124. package/docs/images/content/vscode/v7-choose-agent.png +0 -0
  125. package/docs/images/content/vscode/v7-full.png +0 -0
  126. package/docs/images/content/vscode/v7-onboarding.png +0 -0
  127. package/docs/images/content/vscode/vscode-2-assert.png +0 -0
  128. package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
  129. package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
  130. package/docs/images/content/vscode/vscode-file-creation.png +0 -0
  131. package/docs/images/content/vscode/vscode-install.png +0 -0
  132. package/docs/images/content/vscode/vscode-overview.png +0 -0
  133. package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
  134. package/docs/images/content/vscode/vscode-stopchat.png +0 -0
  135. package/docs/images/content/vscode/vscode-stoptest.png +0 -0
  136. package/docs/images/content/vscode/vscode-tdservice.png +0 -0
  137. package/docs/images/content/vscode/vscode-test-output.png +0 -0
  138. package/docs/images/content/vscode/vscode-testhistory.png +0 -0
  139. package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
  140. package/docs/images/content/vscode/vscode-testpane.png +0 -0
  141. package/docs/images/template/dark.png +0 -0
  142. package/docs/images/template/icon.png +0 -0
  143. package/docs/images/template/light.png +0 -0
  144. package/docs/snippets/calendar-link.mdx +4 -0
  145. package/docs/snippets/gitignore-warning.mdx +7 -0
  146. package/docs/snippets/lifecycle-warning.mdx +6 -0
  147. package/docs/snippets/test-prereqs.mdx +12 -0
  148. package/docs/snippets/tests/assert-replay.mdx +7 -0
  149. package/docs/snippets/tests/assert-yaml.mdx +8 -0
  150. package/docs/snippets/tests/exec-js-replay.mdx +7 -0
  151. package/docs/snippets/tests/exec-js-yaml.mdx +32 -0
  152. package/docs/snippets/tests/exec-shell-replay.mdx +7 -0
  153. package/docs/snippets/tests/exec-shell-yaml.mdx +15 -0
  154. package/docs/snippets/tests/hover-image-replay.mdx +7 -0
  155. package/docs/snippets/tests/hover-image-yaml.mdx +17 -0
  156. package/docs/snippets/tests/hover-text-replay.mdx +7 -0
  157. package/docs/snippets/tests/hover-text-with-description-replay.mdx +7 -0
  158. package/docs/snippets/tests/hover-text-with-description-yaml.mdx +24 -0
  159. package/docs/snippets/tests/hover-text-yaml.mdx +14 -0
  160. package/docs/snippets/tests/match-image-replay.mdx +7 -0
  161. package/docs/snippets/tests/match-image-yaml.mdx +17 -0
  162. package/docs/snippets/tests/press-keys-replay.mdx +7 -0
  163. package/docs/snippets/tests/press-keys-yaml.mdx +36 -0
  164. package/docs/snippets/tests/remember-replay.mdx +7 -0
  165. package/docs/snippets/tests/remember-yaml.mdx +28 -0
  166. package/docs/snippets/tests/scroll-replay.mdx +7 -0
  167. package/docs/snippets/tests/scroll-until-image-replay.mdx +7 -0
  168. package/docs/snippets/tests/scroll-until-image-yaml.mdx +14 -0
  169. package/docs/snippets/tests/scroll-until-text-replay.mdx +7 -0
  170. package/docs/snippets/tests/scroll-until-text-yaml.mdx +17 -0
  171. package/docs/snippets/tests/scroll-yaml.mdx +30 -0
  172. package/docs/snippets/tests/type-repeated-replay.mdx +7 -0
  173. package/docs/snippets/tests/type-repeated-yaml.mdx +22 -0
  174. package/docs/snippets/tests/type-replay.mdx +7 -0
  175. package/docs/snippets/tests/type-yaml.mdx +28 -0
  176. package/docs/snippets/tests/wait-for-image-replay.mdx +7 -0
  177. package/docs/snippets/tests/wait-for-image-yaml.mdx +18 -0
  178. package/docs/snippets/tests/wait-for-text-replay.mdx +7 -0
  179. package/docs/snippets/tests/wait-for-text-yaml.mdx +18 -0
  180. package/docs/snippets/tests/wait-replay.mdx +7 -0
  181. package/docs/snippets/tests/wait-yaml.mdx +13 -0
  182. package/docs/styles.css +65 -0
  183. package/docs/v6/account/dashboard.mdx +16 -0
  184. package/docs/v6/account/enterprise.mdx +110 -0
  185. package/docs/v6/account/pricing.mdx +33 -0
  186. package/docs/v6/account/projects.mdx +33 -0
  187. package/docs/v6/account/team.mdx +35 -0
  188. package/docs/v6/action/ami.mdx +109 -0
  189. package/docs/v6/action/performance.mdx +105 -0
  190. package/docs/v6/action/secrets.mdx +93 -0
  191. package/docs/v6/apps/chrome-extensions.mdx +48 -0
  192. package/docs/v6/apps/desktop-apps.mdx +93 -0
  193. package/docs/v6/apps/mobile-apps.mdx +26 -0
  194. package/docs/v6/apps/static-websites.mdx +54 -0
  195. package/docs/v6/apps/tauri-apps.mdx +361 -0
  196. package/docs/v6/bugs/jira.mdx +232 -0
  197. package/docs/v6/cli/overview.mdx +66 -0
  198. package/docs/v6/commands/assert.mdx +45 -0
  199. package/docs/v6/commands/exec.mdx +276 -0
  200. package/docs/v6/commands/focus-application.mdx +44 -0
  201. package/docs/v6/commands/hover-image.mdx +69 -0
  202. package/docs/v6/commands/hover-text.mdx +47 -0
  203. package/docs/v6/commands/if.mdx +53 -0
  204. package/docs/v6/commands/match-image.mdx +67 -0
  205. package/docs/v6/commands/press-keys.mdx +87 -0
  206. package/docs/v6/commands/remember.mdx +49 -0
  207. package/docs/v6/commands/run.mdx +44 -0
  208. package/docs/v6/commands/scroll-until-image.mdx +66 -0
  209. package/docs/v6/commands/scroll-until-text.mdx +60 -0
  210. package/docs/v6/commands/scroll.mdx +69 -0
  211. package/docs/v6/commands/type.mdx +45 -0
  212. package/docs/v6/commands/wait-for-image.mdx +54 -0
  213. package/docs/v6/commands/wait-for-text.mdx +48 -0
  214. package/docs/v6/commands/wait.mdx +45 -0
  215. package/docs/v6/exporting/junit.mdx +218 -0
  216. package/docs/v6/exporting/playwright.mdx +197 -0
  217. package/docs/v6/features/auto-healing.mdx +144 -0
  218. package/docs/v6/features/generation.mdx +116 -0
  219. package/docs/v6/features/parallel-testing.mdx +151 -0
  220. package/docs/v6/features/reusable-snippets.mdx +131 -0
  221. package/docs/v6/features/selectorless.mdx +80 -0
  222. package/docs/v6/features/visual-assertions.mdx +139 -0
  223. package/docs/v6/getting-started/ci.mdx +146 -0
  224. package/docs/v6/getting-started/cli.mdx +91 -0
  225. package/docs/v6/getting-started/editing.mdx +100 -0
  226. package/docs/v6/getting-started/playwright.mdx +342 -0
  227. package/docs/v6/getting-started/running.mdx +48 -0
  228. package/docs/v6/getting-started/self-hosting.mdx +408 -0
  229. package/docs/v6/getting-started/vscode.mdx +88 -0
  230. package/docs/v6/guide/assertions.mdx +189 -0
  231. package/docs/v6/guide/authentication.mdx +136 -0
  232. package/docs/v6/guide/code.mdx +65 -0
  233. package/docs/v6/guide/dashcam.mdx +118 -0
  234. package/docs/v6/guide/environment-variables.mdx +26 -0
  235. package/docs/v6/guide/lifecycle.mdx +242 -0
  236. package/docs/v6/guide/locating.mdx +141 -0
  237. package/docs/v6/guide/protips.mdx +43 -0
  238. package/docs/v6/guide/variables.mdx +143 -0
  239. package/docs/v6/guide/waiting.mdx +130 -0
  240. package/docs/v6/importing/csv.mdx +196 -0
  241. package/docs/v6/importing/gherkin.mdx +143 -0
  242. package/docs/v6/importing/jira.mdx +164 -0
  243. package/docs/v6/importing/testrail.mdx +162 -0
  244. package/docs/v6/integrations/electron.mdx +146 -0
  245. package/docs/v6/integrations/netlify.mdx +100 -0
  246. package/docs/v6/integrations/vercel.mdx +125 -0
  247. package/docs/v6/interactive/explore.mdx +99 -0
  248. package/docs/v6/interactive/run.mdx +52 -0
  249. package/docs/v6/interactive/save.mdx +63 -0
  250. package/docs/v6/overview/comparison.mdx +101 -0
  251. package/docs/v6/overview/faq.mdx +162 -0
  252. package/docs/v6/overview/performance.mdx +52 -0
  253. package/docs/v6/overview/quickstart.mdx +137 -0
  254. package/docs/v6/overview/what-is-testdriver.mdx +85 -0
  255. package/docs/v6/scenarios/ai-chatbot.mdx +28 -0
  256. package/docs/v6/scenarios/cookie-banner.mdx +32 -0
  257. package/docs/v6/scenarios/file-upload.mdx +33 -0
  258. package/docs/v6/scenarios/form-filling.mdx +32 -0
  259. package/docs/v6/scenarios/log-in.mdx +75 -0
  260. package/docs/v6/scenarios/pdf-generation.mdx +25 -0
  261. package/docs/v6/scenarios/spell-check.mdx +22 -0
  262. package/docs/v6/security/action.mdx +84 -0
  263. package/docs/v6/security/agent.mdx +73 -0
  264. package/docs/v6/security/platform.mdx +77 -0
  265. package/docs/v6/tutorials/advanced-test.mdx +81 -0
  266. package/docs/v6/tutorials/basic-test.mdx +45 -0
  267. package/docs/v7/_drafts/agents.mdx +843 -0
  268. package/docs/v7/_drafts/architecture.mdx +399 -0
  269. package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
  270. package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +100 -0
  271. package/docs/v7/_drafts/best-practices.mdx +486 -0
  272. package/docs/v7/_drafts/caching-ai.mdx +215 -0
  273. package/docs/v7/_drafts/caching-selectors.mdx +424 -0
  274. package/docs/v7/_drafts/caching.mdx +366 -0
  275. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
  276. package/docs/v7/_drafts/commands/assert.mdx +45 -0
  277. package/docs/v7/_drafts/commands/exec.mdx +276 -0
  278. package/docs/v7/_drafts/commands/focus-application.mdx +44 -0
  279. package/docs/v7/_drafts/commands/hover-image.mdx +69 -0
  280. package/docs/v7/_drafts/commands/hover-text.mdx +47 -0
  281. package/docs/v7/_drafts/commands/if.mdx +53 -0
  282. package/docs/v7/_drafts/commands/match-image.mdx +67 -0
  283. package/docs/v7/_drafts/commands/press-keys.mdx +87 -0
  284. package/docs/v7/_drafts/commands/remember.mdx +49 -0
  285. package/docs/v7/_drafts/commands/run.mdx +44 -0
  286. package/docs/v7/_drafts/commands/scroll-until-image.mdx +66 -0
  287. package/docs/v7/_drafts/commands/scroll-until-text.mdx +60 -0
  288. package/docs/v7/_drafts/commands/scroll.mdx +69 -0
  289. package/docs/v7/_drafts/commands/type.mdx +45 -0
  290. package/docs/v7/_drafts/commands/wait-for-image.mdx +54 -0
  291. package/docs/v7/_drafts/commands/wait-for-text.mdx +48 -0
  292. package/docs/v7/_drafts/commands/wait.mdx +45 -0
  293. package/docs/v7/_drafts/configuration.mdx +378 -0
  294. package/docs/v7/_drafts/contributing.mdx +174 -0
  295. package/docs/v7/_drafts/core.mdx +458 -0
  296. package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
  297. package/docs/v7/_drafts/debugging.mdx +349 -0
  298. package/docs/v7/_drafts/error-handling.mdx +501 -0
  299. package/docs/v7/_drafts/faq.mdx +393 -0
  300. package/docs/v7/_drafts/hooks.mdx +360 -0
  301. package/docs/v7/_drafts/init-command.mdx +95 -0
  302. package/docs/v7/_drafts/installation.mdx +420 -0
  303. package/docs/v7/_drafts/migration.mdx +562 -0
  304. package/docs/v7/_drafts/observable.mdx +604 -0
  305. package/docs/v7/_drafts/playwright.mdx +342 -0
  306. package/docs/v7/_drafts/plugin-migration.mdx +220 -0
  307. package/docs/v7/_drafts/powerful.mdx +419 -0
  308. package/docs/v7/_drafts/presets.mdx +210 -0
  309. package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
  310. package/docs/v7/_drafts/prompt-cache.mdx +200 -0
  311. package/docs/v7/_drafts/provision.mdx +390 -0
  312. package/docs/v7/_drafts/quick-start-test-recording.mdx +214 -0
  313. package/docs/v7/_drafts/readme.mdx +135 -0
  314. package/docs/v7/_drafts/reports.mdx +414 -0
  315. package/docs/v7/_drafts/scalable.mdx +763 -0
  316. package/docs/v7/_drafts/screenshot.mdx +155 -0
  317. package/docs/v7/_drafts/sdk-awesome-logs.mdx +468 -0
  318. package/docs/v7/_drafts/sdk-browser-rendering.mdx +167 -0
  319. package/docs/v7/_drafts/sdk-migration.mdx +474 -0
  320. package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
  321. package/docs/v7/_drafts/self-hosting.mdx +369 -0
  322. package/docs/v7/_drafts/test-recording.mdx +382 -0
  323. package/docs/v7/_drafts/troubleshooting.mdx +526 -0
  324. package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
  325. package/docs/v7/_drafts/vitest.mdx +535 -0
  326. package/docs/v7/_drafts/writing-tests.mdx +25 -0
  327. package/docs/v7/ai.mdx +205 -0
  328. package/docs/v7/assert.mdx +316 -0
  329. package/docs/v7/aws-setup.mdx +449 -0
  330. package/docs/v7/cache.mdx +223 -0
  331. package/docs/v7/caching.mdx +128 -0
  332. package/docs/v7/captcha.mdx +159 -0
  333. package/docs/v7/ci-cd.mdx +603 -0
  334. package/docs/v7/click.mdx +287 -0
  335. package/docs/v7/client.mdx +478 -0
  336. package/docs/v7/copilot/auto-healing.mdx +265 -0
  337. package/docs/v7/copilot/creating-tests.mdx +156 -0
  338. package/docs/v7/copilot/github.mdx +143 -0
  339. package/docs/v7/copilot/running-tests.mdx +149 -0
  340. package/docs/v7/copilot/setup.mdx +143 -0
  341. package/docs/v7/customizing-devices.mdx +319 -0
  342. package/docs/v7/dashcam.mdx +419 -0
  343. package/docs/v7/debugging-with-screenshots.mdx +402 -0
  344. package/docs/v7/device-config.mdx +317 -0
  345. package/docs/v7/double-click.mdx +102 -0
  346. package/docs/v7/elements.mdx +606 -0
  347. package/docs/v7/enterprise.mdx +9 -0
  348. package/docs/v7/errors.mdx +248 -0
  349. package/docs/v7/events.mdx +358 -0
  350. package/docs/v7/examples/ai.mdx +72 -0
  351. package/docs/v7/examples/assert.mdx +72 -0
  352. package/docs/v7/examples/captcha-api.mdx +92 -0
  353. package/docs/v7/examples/chrome-extension.mdx +132 -0
  354. package/docs/v7/examples/drag-and-drop.mdx +100 -0
  355. package/docs/v7/examples/element-not-found.mdx +67 -0
  356. package/docs/v7/examples/exec-output.mdx +85 -0
  357. package/docs/v7/examples/exec-pwsh.mdx +83 -0
  358. package/docs/v7/examples/focus-window.mdx +62 -0
  359. package/docs/v7/examples/hover-image.mdx +94 -0
  360. package/docs/v7/examples/hover-text.mdx +69 -0
  361. package/docs/v7/examples/installer.mdx +91 -0
  362. package/docs/v7/examples/launch-vscode-linux.mdx +101 -0
  363. package/docs/v7/examples/match-image.mdx +96 -0
  364. package/docs/v7/examples/press-keys.mdx +92 -0
  365. package/docs/v7/examples/scroll-keyboard.mdx +79 -0
  366. package/docs/v7/examples/scroll-until-image.mdx +81 -0
  367. package/docs/v7/examples/scroll-until-text.mdx +109 -0
  368. package/docs/v7/examples/scroll.mdx +81 -0
  369. package/docs/v7/examples/type.mdx +92 -0
  370. package/docs/v7/examples/windows-installer.mdx +89 -0
  371. package/docs/v7/exec.mdx +318 -0
  372. package/docs/v7/find.mdx +830 -0
  373. package/docs/v7/focus-application.mdx +294 -0
  374. package/docs/v7/generating-tests.mdx +36 -0
  375. package/docs/v7/hosted.mdx +158 -0
  376. package/docs/v7/hover.mdx +279 -0
  377. package/docs/v7/locating-elements.mdx +71 -0
  378. package/docs/v7/making-assertions.mdx +32 -0
  379. package/docs/v7/mcp.mdx +9 -0
  380. package/docs/v7/mouse-down.mdx +161 -0
  381. package/docs/v7/mouse-up.mdx +164 -0
  382. package/docs/v7/parse.mdx +237 -0
  383. package/docs/v7/performing-actions.mdx +54 -0
  384. package/docs/v7/press-keys.mdx +349 -0
  385. package/docs/v7/provision.mdx +333 -0
  386. package/docs/v7/quickstart.mdx +173 -0
  387. package/docs/v7/redraw.mdx +216 -0
  388. package/docs/v7/reusable-code.mdx +249 -0
  389. package/docs/v7/right-click.mdx +123 -0
  390. package/docs/v7/running-tests.mdx +185 -0
  391. package/docs/v7/screenshot.mdx +249 -0
  392. package/docs/v7/screenshots.mdx +186 -0
  393. package/docs/v7/scroll.mdx +336 -0
  394. package/docs/v7/secrets.mdx +115 -0
  395. package/docs/v7/self-hosted.mdx +149 -0
  396. package/docs/v7/type.mdx +358 -0
  397. package/docs/v7/variables.mdx +111 -0
  398. package/docs/v7/wait.mdx +52 -0
  399. package/docs/v7/waiting-for-elements.mdx +90 -0
  400. package/docs/v7/what-is-testdriver.mdx +54 -0
  401. package/eslint.config.js +67 -0
  402. package/examples/ai.test.mjs +31 -0
  403. package/examples/assert.test.mjs +47 -0
  404. package/examples/chrome-extension.test.mjs +97 -0
  405. package/examples/config.mjs +5 -0
  406. package/examples/element-not-found.test.mjs +27 -0
  407. package/examples/exec-output.test.mjs +60 -0
  408. package/examples/exec-pwsh.test.mjs +58 -0
  409. package/examples/findall-coffee-icons.test.mjs +42 -0
  410. package/examples/focus-window.test.mjs +37 -0
  411. package/examples/formatted-logging.test.mjs +27 -0
  412. package/examples/hover-image.test.mjs +53 -0
  413. package/examples/hover-text-with-description.test.mjs +57 -0
  414. package/examples/hover-text.test.mjs +28 -0
  415. package/examples/installer.test.mjs +50 -0
  416. package/examples/launch-vscode-linux.test.mjs +55 -0
  417. package/examples/match-image.test.mjs +55 -0
  418. package/examples/parse.test.mjs +19 -0
  419. package/examples/press-keys.test.mjs +44 -0
  420. package/examples/prompt.test.mjs +34 -0
  421. package/examples/scroll-keyboard.test.mjs +38 -0
  422. package/examples/scroll-until-image.test.mjs +40 -0
  423. package/examples/scroll.test.mjs +42 -0
  424. package/examples/type.test.mjs +46 -0
  425. package/examples/windows-installer.test.mjs +54 -0
  426. package/index.js +2 -0
  427. package/interfaces/cli/commands/init.js +438 -0
  428. package/interfaces/cli/commands/setup.js +382 -0
  429. package/interfaces/cli/lib/base.js +285 -0
  430. package/interfaces/cli.js +20 -0
  431. package/interfaces/junit-reporter.js +290 -0
  432. package/interfaces/logger.js +388 -0
  433. package/interfaces/readline.js +234 -0
  434. package/interfaces/shared-test-state.mjs +64 -0
  435. package/interfaces/vitest-plugin.d.ts +115 -0
  436. package/interfaces/vitest-plugin.mjs +1698 -0
  437. package/lib/captcha/solver.js +358 -0
  438. package/lib/core/Dashcam.js +533 -0
  439. package/lib/core/index.d.ts +172 -0
  440. package/lib/core/index.js +12 -0
  441. package/lib/environments.json +18 -0
  442. package/lib/github-comment-formatter.js +263 -0
  443. package/lib/github-comment.mjs +452 -0
  444. package/lib/init-project.js +575 -0
  445. package/lib/presets/index.mjs +331 -0
  446. package/lib/resolve-channel.js +46 -0
  447. package/lib/sentry.js +417 -0
  448. package/lib/vitest/hooks.d.ts +57 -0
  449. package/lib/vitest/hooks.mjs +674 -0
  450. package/lib/vitest/setup-aws.mjs +247 -0
  451. package/lib/vitest/setup-self-hosted.mjs +151 -0
  452. package/lib/vitest/setup.mjs +46 -0
  453. package/manual/captcha-api.test.mjs +51 -0
  454. package/manual/drag-and-drop.test.mjs +59 -0
  455. package/manual/flake-diffthreshold-001.test.mjs +9 -0
  456. package/manual/flake-diffthreshold-01.test.mjs +9 -0
  457. package/manual/flake-diffthreshold-05.test.mjs +9 -0
  458. package/manual/flake-noredraw-cache.test.mjs +9 -0
  459. package/manual/flake-noredraw-nocache.test.mjs +9 -0
  460. package/manual/flake-redraw-cache.test.mjs +9 -0
  461. package/manual/flake-redraw-nocache.test.mjs +9 -0
  462. package/manual/flake-rocket-match.test.mjs +30 -0
  463. package/manual/flake-shared.mjs +51 -0
  464. package/manual/no-provision.test.mjs +31 -0
  465. package/manual/packer-hover-image.test.mjs +176 -0
  466. package/manual/scroll-until-text.test.mjs +68 -0
  467. package/manual/test-init-command.js +223 -0
  468. package/mcp-server/README.md +322 -0
  469. package/mcp-server/dist/codegen.d.ts +9 -0
  470. package/mcp-server/dist/codegen.js +165 -0
  471. package/mcp-server/dist/mcp-app.html +114 -0
  472. package/mcp-server/dist/package.json +1 -0
  473. package/mcp-server/dist/provision-types.d.ts +290 -0
  474. package/mcp-server/dist/provision-types.js +174 -0
  475. package/mcp-server/dist/server.d.ts +6 -0
  476. package/mcp-server/dist/server.mjs +1925 -0
  477. package/mcp-server/dist/session.d.ts +85 -0
  478. package/mcp-server/dist/session.js +152 -0
  479. package/mcp-server/mcp-app.html +28 -0
  480. package/mcp-server/mcp-config.example.json +19 -0
  481. package/mcp-server/package-lock.json +4027 -0
  482. package/mcp-server/package.json +31 -0
  483. package/mcp-server/src/codegen.ts +189 -0
  484. package/mcp-server/src/mcp-app.css +360 -0
  485. package/mcp-server/src/mcp-app.ts +547 -0
  486. package/mcp-server/src/provision-types.ts +209 -0
  487. package/mcp-server/src/server.ts +2391 -0
  488. package/mcp-server/src/session.ts +194 -0
  489. package/mcp-server/tsconfig.json +16 -0
  490. package/mcp-server/vite.config.ts +23 -0
  491. package/package.json +158 -0
  492. package/schema.json +1046 -0
  493. package/scripts/generate-skills.js +94 -0
  494. package/sdk-log-formatter.js +1157 -0
  495. package/sdk.d.ts +1486 -0
  496. package/sdk.js +4336 -0
  497. package/setup/aws/cloudformation.yaml +463 -0
  498. package/setup/aws/disable-defender.sh +42 -0
  499. package/setup/aws/install-dev-runner.sh +79 -0
  500. package/setup/aws/spawn-runner.sh +289 -0
  501. package/test/captcha-solver.test.mjs +152 -0
  502. package/test/chrome-remote-debugging.test.mjs +66 -0
  503. package/test/duckduckgo/experiment.test.mjs +28 -0
  504. package/test/duckduckgo/setup.test.mjs +29 -0
  505. package/test/manual/debug-locate-response.js +82 -0
  506. package/test/manual/reconnect-provision.test.mjs +49 -0
  507. package/test/manual/test-console-logs.test.mjs +42 -0
  508. package/test/manual/test-find-api.js +73 -0
  509. package/test/manual/test-init.sh +54 -0
  510. package/test/manual/test-prompt-cache.js +97 -0
  511. package/test/manual/test-provision-auth.mjs +22 -0
  512. package/test/manual/test-sandbox-render.js +29 -0
  513. package/test/manual/test-sdk-methods.js +15 -0
  514. package/test/manual/test-sdk-refactor.js +53 -0
  515. package/test/manual/test-stack-trace.mjs +57 -0
  516. package/test/manual/verify-element-api.js +89 -0
  517. package/test/manual/verify-types.js +0 -0
  518. package/test/manual-unawaited-promise.test.mjs +31 -0
  519. package/vitest.config.mjs +58 -0
  520. package/vitest.runner.config.mjs +33 -0
  521. package/vscode-extension/.vscodeignore +12 -0
  522. package/vscode-extension/README.md +94 -0
  523. package/vscode-extension/media/icon.png +0 -0
  524. package/vscode-extension/package-lock.json +4126 -0
  525. package/vscode-extension/package.json +86 -0
  526. package/vscode-extension/src/extension.ts +829 -0
  527. package/vscode-extension/testdriverai-0.1.0.vsix +0 -0
  528. package/vscode-extension/tsconfig.json +16 -0
@@ -0,0 +1,533 @@
1
+ /**
2
+ * Dashcam Class
3
+ * Manages Dashcam CLI recording lifecycle
4
+ *
5
+ * Provides a clean interface for:
6
+ * - Authentication
7
+ * - Log tracking
8
+ * - Starting/stopping recordings
9
+ * - Retrieving replay URLs
10
+ */
11
+
12
+ const { logger } = require("../../interfaces/logger");
13
+
14
+ class Dashcam {
15
+ /**
16
+ * Create a Dashcam instance
17
+ * @param {Object} client - TestDriver client instance
18
+ * @param {Object} options - Configuration options
19
+ * @param {string} [options.apiKey] - Dashcam API key
20
+ * @param {boolean} [options.autoStart=false] - Auto-start recording
21
+ * @param {Array} [options.logs=[]] - Log configurations to add
22
+ * @param {string} [options.title] - Recording title (defaults to generated title)
23
+ */
24
+ constructor(client, options = {}) {
25
+ if (!client) {
26
+ throw new Error("Dashcam requires a TestDriver client instance");
27
+ }
28
+
29
+ this.client = client;
30
+ // Use provided apiKey, or client's apiKey, or fallback to a default
31
+ this.apiKey =
32
+ options.apiKey ||
33
+ client.apiKey ||
34
+ client.config?.TD_API_KEY ||
35
+ "4e93d8bf-3886-4d26-a144-116c4063522d";
36
+ this.autoStart = options.autoStart ?? false;
37
+ this.logs = options.logs || [];
38
+ this.recording = false;
39
+ this._authenticated = false;
40
+ this.startTime = null; // Track when dashcam recording started
41
+ this.title = options.title || this._generateDefaultTitle();
42
+ }
43
+
44
+ /**
45
+ * Generate a default title for the recording
46
+ * Uses test context if available, otherwise falls back to timestamp
47
+ * @private
48
+ */
49
+ _generateDefaultTitle() {
50
+ // Check for Vitest context
51
+ if (this.client.__vitestContext) {
52
+ const task = this.client.__vitestContext;
53
+ const testName = task.name || "Test";
54
+ const fileName = task.file?.name || task.file?.filepath;
55
+ if (fileName) {
56
+ const baseName = fileName
57
+ .split("/")
58
+ .pop()
59
+ .replace(/\.(test|spec)\.(js|mjs|ts|tsx)$/, "");
60
+ return `${baseName} - ${testName}`;
61
+ }
62
+ return testName;
63
+ }
64
+
65
+ // Fallback to timestamp
66
+ const now = new Date();
67
+ return `Recording ${now.toISOString().replace(/T/, " ").replace(/\..+/, "")}`;
68
+ }
69
+
70
+ /**
71
+ * Get shell type based on client OS
72
+ * @private
73
+ */
74
+ _getShell() {
75
+ return this.client.os === "windows" ? "pwsh" : "sh";
76
+ }
77
+
78
+ /**
79
+ * Get TD_API_ROOT from client config
80
+ * @private
81
+ */
82
+ _getApiRoot() {
83
+ const channelConfig = require("../../lib/resolve-channel.js");
84
+ return (
85
+ this.client.config?.TD_API_ROOT || channelConfig.channels[channelConfig.active]
86
+ );
87
+ }
88
+
89
+ /**
90
+ * Get console URL based on API root
91
+ * Maps API endpoints to their corresponding web console URLs
92
+ * @param {string} apiRoot - The API root URL
93
+ * @returns {string} The corresponding console URL
94
+ */
95
+ static getConsoleUrl(apiRoot = (() => { const c = require("../../lib/resolve-channel.js"); return c.channels[c.active]; })()) {
96
+ // Allow explicit override via env (e.g. VITE_DOMAIN from .env)
97
+ if (process.env.VITE_DOMAIN) return process.env.VITE_DOMAIN;
98
+
99
+ if (!apiRoot) return "https://console.testdriver.ai";
100
+
101
+ // Dash-separated environments: api-{env}.testdriver.ai -> console-{env}.testdriver.ai
102
+ const envMatch = apiRoot.match(/^https:\/\/api-(test|canary)\.testdriver\.ai/);
103
+ if (envMatch) {
104
+ return `https://console-${envMatch[1]}.testdriver.ai`;
105
+ }
106
+
107
+ // Production: API on custom domain or v6 -> Console on testdriver.ai
108
+ if (
109
+ apiRoot.includes("api.testdriver.ai") ||
110
+ apiRoot.includes("v6.testdriver.ai")
111
+ ) {
112
+ return "https://console.testdriver.ai";
113
+ }
114
+
115
+ // Local development via ngrok -> localhost web app
116
+ if (apiRoot.includes("ngrok.io")) {
117
+ return "http://localhost:3001";
118
+ }
119
+
120
+ // Fly.io PR previews: map API app to Web app
121
+ // pr-123-api.fly.dev -> pr-123-web.fly.dev
122
+ const flyPrMatch = apiRoot.match(/https:\/\/(pr-\d+)-api\.fly\.dev/);
123
+ if (flyPrMatch) {
124
+ const [, prPrefix] = flyPrMatch;
125
+ return `https://${prPrefix}-web.fly.dev`;
126
+ }
127
+
128
+ // Fly.io environment apps: test-api.fly.dev -> test-web.fly.dev
129
+ const flyEnvMatch = apiRoot.match(/https:\/\/([\w-]+)-api\.fly\.dev/);
130
+ if (flyEnvMatch) {
131
+ const [, prefix] = flyEnvMatch;
132
+ return `https://${prefix}-web.fly.dev`;
133
+ }
134
+
135
+ // Render PR previews: map API service to Web service
136
+ // canary-api-pr-123.onrender.com -> canary-web-pr-123.onrender.com
137
+ // testdriver-api-i4m4-pr-123.onrender.com -> web-i4m4-pr-123.onrender.com
138
+ const renderPrMatch = apiRoot.match(/https:\/\/([\w-]+)-api(-[\w]+)?(-pr-\d+)?\.onrender\.com/);
139
+ if (renderPrMatch) {
140
+ const [, prefix, suffix, prSuffix] = renderPrMatch;
141
+ let webPrefix;
142
+ if (prefix === 'testdriver' && suffix) {
143
+ webPrefix = 'web' + suffix;
144
+ } else {
145
+ webPrefix = prefix + '-web';
146
+ }
147
+ return `https://${webPrefix}${prSuffix || ''}.onrender.com`;
148
+ }
149
+
150
+ // Cloudflare tunnels, custom domains, etc.: the web console is served
151
+ // from the same origin as the API, so return apiRoot as-is.
152
+ return apiRoot;
153
+ }
154
+
155
+ /**
156
+ * Get dashcam executable path
157
+ * @private
158
+ */
159
+ async _getDashcamPath() {
160
+
161
+ if (this.client.os === "windows") {
162
+ return "C:\\Program Files\\nodejs\\dashcam.cmd";
163
+ } else {
164
+ return "/usr/bin/dashcam";
165
+ }
166
+
167
+ }
168
+
169
+ /**
170
+ * Authenticate dashcam with API key
171
+ * @param {string} [apiKey] - Override API key
172
+ * @returns {Promise<void>}
173
+ */
174
+ async auth(apiKey) {
175
+ const key = apiKey || this.apiKey;
176
+ const shell = this._getShell();
177
+ const apiRoot = this._getApiRoot();
178
+
179
+ if (this.client.os === "windows") {
180
+ const dashcamPath = await this._getDashcamPath();
181
+ this._log("debug", "Dashcam executable path:", dashcamPath);
182
+
183
+ // Authenticate with TD_API_ROOT
184
+ const authOutput = await this.client.exec(
185
+ shell,
186
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" auth ${key}`,
187
+ 120000,
188
+ process.env.TD_DEBUG == "true" ? false : true,
189
+ );
190
+ this._log("debug", "Auth output:", authOutput);
191
+ } else {
192
+ // Linux/Mac authentication with TD_API_ROOT
193
+ const authOutput = await this.client.exec(
194
+ shell,
195
+ `TD_API_ROOT="${apiRoot}" dashcam auth ${key}`,
196
+ 120000,
197
+ process.env.TD_DEBUG == "true" ? false : true,
198
+ );
199
+ this._log("debug", "Auth output:", authOutput);
200
+ }
201
+
202
+ this._authenticated = true;
203
+ }
204
+
205
+ /**
206
+ * Add file log tracking
207
+ * @param {string} path - Path to log file
208
+ * @param {string} name - Display name
209
+ * @returns {Promise<void>}
210
+ */
211
+ async addFileLog(path, name) {
212
+ const shell = this._getShell();
213
+ const apiRoot = this._getApiRoot();
214
+
215
+ if (this.client.os === "windows") {
216
+ // Create log file if it doesn't exist
217
+ const createFileOutput = await this.client.exec(
218
+ shell,
219
+ `New-Item -ItemType File -Path "${path}" -Force`,
220
+ 10000,
221
+ process.env.TD_DEBUG == "true" ? false : true,
222
+ );
223
+ this._log("debug", "Create log file output:", createFileOutput);
224
+
225
+ const dashcamPath = await this._getDashcamPath();
226
+ const addLogOutput = await this.client.exec(
227
+ shell,
228
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=file --file="${path}" --name="${name}"`,
229
+ 120000,
230
+ process.env.TD_DEBUG == "true" ? false : true,
231
+ );
232
+ this._log("debug", "Add log tracking output:", addLogOutput);
233
+ } else {
234
+ // Create log file
235
+ await this.client.exec(
236
+ shell,
237
+ `touch ${path}`,
238
+ 10000,
239
+ process.env.TD_DEBUG == "true" ? false : true,
240
+ );
241
+
242
+ // Add log tracking with TD_API_ROOT
243
+ const addLogOutput = await this.client.exec(
244
+ shell,
245
+ `TD_API_ROOT="${apiRoot}" dashcam logs --add --type=file --file="${path}" --name="${name}"`,
246
+ 10000,
247
+ process.env.TD_DEBUG == "true" ? false : true,
248
+ );
249
+ this._log("debug", "Add log tracking output:", addLogOutput);
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Add application log tracking
255
+ * @param {string} application - Application name
256
+ * @param {string} name - Display name
257
+ * @returns {Promise<void>}
258
+ */
259
+ async addApplicationLog(application, name) {
260
+ const shell = this._getShell();
261
+ const dashcamPath = await this._getDashcamPath();
262
+ const apiRoot = this._getApiRoot();
263
+
264
+ if (this.client.os === "windows") {
265
+ const addLogOutput = await this.client.exec(
266
+ shell,
267
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=application --application="${application}" --name="${name}"`,
268
+ 120000,
269
+ process.env.TD_DEBUG == "true" ? false : true,
270
+ );
271
+ this._log("debug", "Add application log tracking output:", addLogOutput);
272
+ } else {
273
+ const addLogOutput = await this.client.exec(
274
+ shell,
275
+ `TD_API_ROOT="${apiRoot}" dashcam logs --add --type=application --application="${application}" --name="${name}"`,
276
+ 10000,
277
+ process.env.TD_DEBUG == "true" ? false : true,
278
+ );
279
+ this._log("debug", "Add application log tracking output:", addLogOutput);
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Add web log tracking
285
+ * @param {string} pattern - URL pattern to match (e.g., "*example.com*")
286
+ * @param {string} name - Display name
287
+ * @returns {Promise<void>}
288
+ */
289
+ async addWebLog(pattern, name) {
290
+ const shell = this._getShell();
291
+ const dashcamPath = await this._getDashcamPath();
292
+ const apiRoot = this._getApiRoot();
293
+
294
+ if (this.client.os === "windows") {
295
+ try {
296
+ const addLogOutput = await this.client.exec(
297
+ shell,
298
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=web --pattern="${pattern}" --name="${name}"`,
299
+ 120000,
300
+ process.env.TD_DEBUG == "true" ? false : true,
301
+ );
302
+ this._log("debug", "Add web log tracking output:", addLogOutput);
303
+ } catch (err) {
304
+ this._log("warn", "Add web log tracking failed:", err.message);
305
+ }
306
+ } else {
307
+ const addLogOutput = await this.client.exec(
308
+ shell,
309
+ `TD_API_ROOT="${apiRoot}" dashcam logs --add --type=web --pattern="${pattern}" --name="${name}"`,
310
+ 10000,
311
+ process.env.TD_DEBUG == "true" ? false : true,
312
+ );
313
+ this._log("debug", "Add web log tracking output:", addLogOutput);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Start dashcam recording
319
+ * @returns {Promise<void>}
320
+ */
321
+ async start() {
322
+ if (this.recording) {
323
+ this._log("warn", "Dashcam already recording");
324
+ return;
325
+ }
326
+
327
+ // Auto-authenticate if not already done
328
+ if (!this._authenticated) {
329
+ await this.auth();
330
+ }
331
+
332
+ const shell = this._getShell();
333
+ const apiRoot = this._getApiRoot();
334
+
335
+ if (this.client.os === "windows") {
336
+ const dashcamPath = await this._getDashcamPath();
337
+
338
+ // Start dashcam record and redirect output with TD_API_ROOT
339
+ const outputFile =
340
+ "C:\\Users\\testdriver\\.dashcam-cli\\dashcam-start.log";
341
+ // const titleArg = this.title ? ` --title=\`"${this.title.replace(/"/g, '`"')}\`"` : '';
342
+ let titleArg = "";
343
+ const startScript = `
344
+ try {
345
+ $env:TD_API_ROOT="${apiRoot}"
346
+ $process = Start-Process "cmd.exe" -ArgumentList "/c", "\`"${dashcamPath}\`" record${titleArg}"
347
+ Write-Output "Process started with PID: $($process.Id)"
348
+ Start-Sleep -Seconds 2
349
+ if ($process.HasExited) {
350
+ Write-Output "Process has already exited with code: $($process.ExitCode)"
351
+ } else {
352
+ Write-Output "Process is still running"
353
+ }
354
+ } catch {
355
+ Write-Output "ERROR: $_"
356
+ }
357
+ `;
358
+
359
+ // add 2>&1" -PassThru
360
+
361
+ // Capture startTime right before issuing the dashcam command to sync with actual recording start
362
+ this.startTime = Date.now();
363
+ const startOutput = await this.client.exec(
364
+ shell,
365
+ startScript,
366
+ 10000,
367
+ process.env.TD_DEBUG == "true" ? false : true,
368
+ );
369
+ this._log("debug", "Start-Process output:", startOutput);
370
+
371
+ // Wait and check output
372
+ await new Promise((resolve) => setTimeout(resolve, 2000));
373
+ const dashcamOutput = await this.client.exec(
374
+ shell,
375
+ `Get-Content "${outputFile}" -ErrorAction SilentlyContinue`,
376
+ 10000,
377
+ process.env.TD_DEBUG == "true" ? false : true,
378
+ );
379
+ this._log("debug", "Dashcam record output:", dashcamOutput);
380
+
381
+ // Give process time to initialize
382
+ await new Promise((resolve) => setTimeout(resolve, 5000));
383
+
384
+ this._log("debug", "Dashcam recording started");
385
+ } else {
386
+ // Linux/Mac with TD_API_ROOT
387
+ this._log("debug", "Starting dashcam recording on Linux/Mac...");
388
+ const titleArg = this.title
389
+ ? ` --title="${this.title.replace(/"/g, '"')}"`
390
+ : "";
391
+ // Capture startTime right before issuing the dashcam command to sync with actual recording start
392
+ this.startTime = Date.now();
393
+ await this.client.exec(
394
+ shell,
395
+ `TD_API_ROOT="${apiRoot}" dashcam record${titleArg} >/dev/null 2>&1 &`,
396
+ 10000,
397
+ process.env.TD_DEBUG == "true" ? false : true,
398
+ );
399
+ this._log("debug", "Dashcam recording started");
400
+ }
401
+
402
+ this.recording = true;
403
+ }
404
+
405
+ /**
406
+ * Set the recording title
407
+ * This can be called before start() to customize the title
408
+ * @param {string} title - Custom recording title
409
+ */
410
+ setTitle(title) {
411
+ this.title = title;
412
+ this._log("debug", `Set dashcam recording title: ${title}`);
413
+ }
414
+
415
+ /**
416
+ * Stop dashcam recording and retrieve replay URL
417
+ * @returns {Promise<string|null>} Replay URL if available
418
+ */
419
+ async stop() {
420
+ if (!this.recording) {
421
+ // Internal log only - don't spam user console
422
+ this._log("warn", "Dashcam not recording");
423
+ return null;
424
+ }
425
+
426
+ this._log("debug", "Stopping dashcam and retrieving URL...");
427
+ const shell = this._getShell();
428
+ const apiRoot = this._getApiRoot();
429
+ let output;
430
+
431
+ if (this.client.os === "windows") {
432
+ this._log("debug", "Stopping dashcam process on Windows...");
433
+
434
+ const dashcamPath = await this._getDashcamPath();
435
+
436
+ // Stop and get output with TD_API_ROOT
437
+ output = await this.client.exec(
438
+ shell,
439
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" stop`,
440
+ 300000,
441
+ process.env.TD_DEBUG == "true" ? false : true,
442
+ );
443
+ this._log("debug", "Dashcam stop command output:", output);
444
+ } else {
445
+ // Linux/Mac with TD_API_ROOT
446
+ const dashcamPath = await this._getDashcamPath();
447
+ output = await this.client.exec(
448
+ shell,
449
+ `TD_API_ROOT="${apiRoot}" "${dashcamPath}" stop`,
450
+ 300000,
451
+ process.env.TD_DEBUG == "true" ? false : true,
452
+ );
453
+ this._log("debug", "Dashcam command output:", output);
454
+ }
455
+
456
+ this.recording = false;
457
+
458
+ // Extract the /replay/... path from CLI output and reconstruct the URL
459
+ // using getConsoleUrl(). The CLI may return a wrong domain
460
+ // so we always rewrite the base URL to match the current environment.
461
+ if (output) {
462
+ // Match /replay/{id} with optional query params from any URL or broken prefix
463
+ const replayPathMatch = output.match(
464
+ /(?:https?:\/\/[^\s"',}]+|undefined|null)?(\/replay\/[^\s"',}]+)/,
465
+ );
466
+ if (replayPathMatch) {
467
+ const replayPath = replayPathMatch[1].replace(/[.,;:!\)\]]+$/, "").trim();
468
+ const consoleUrl = Dashcam.getConsoleUrl(this._getApiRoot());
469
+ const url = consoleUrl + replayPath;
470
+ this._log("debug", "Replay URL:", url);
471
+ return url;
472
+ }
473
+
474
+ this._log("warn", "No replay URL found in dashcam output");
475
+ } else {
476
+ this._log("warn", "Dashcam command returned no output");
477
+ }
478
+
479
+ return null;
480
+ }
481
+
482
+ /**
483
+ * Internal logging - uses TestDriver logger
484
+ * @private
485
+ */
486
+ _log(level, ...args) {
487
+ const message = args
488
+ .map((arg) =>
489
+ typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg),
490
+ )
491
+ .join(" ");
492
+
493
+ const logMessage = `[DASHCAM] ${message}`;
494
+
495
+ // Use the TestDriver logger based on level
496
+ switch (level) {
497
+ case "error":
498
+ logger.error(logMessage);
499
+ break;
500
+ case "warn":
501
+ logger.warn(logMessage);
502
+ break;
503
+ case "debug":
504
+ logger.debug(logMessage);
505
+ break;
506
+ case "info":
507
+ default:
508
+ logger.info(logMessage);
509
+ break;
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Check if currently recording
515
+ * @returns {Promise<boolean>}
516
+ */
517
+ async isRecording() {
518
+ return this.recording;
519
+ }
520
+
521
+ /**
522
+ * Get milliseconds elapsed since dashcam started recording
523
+ * @returns {number|null} Milliseconds since start, or null if not recording
524
+ */
525
+ getElapsedTime() {
526
+ if (!this.recording || !this.startTime) {
527
+ return null;
528
+ }
529
+ return Date.now() - this.startTime;
530
+ }
531
+ }
532
+
533
+ module.exports = Dashcam;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * TypeScript definitions for TestDriver Core Module
3
+ * @module testdriverai/core
4
+ */
5
+
6
+ export class Dashcam {
7
+ /**
8
+ * Create a new Dashcam instance
9
+ * @param client - TestDriver client instance
10
+ * @param options - Dashcam options
11
+ */
12
+ constructor(client: any, options?: DashcamOptions);
13
+
14
+ /**
15
+ * Authenticate with Dashcam CLI
16
+ * @param apiKey - Dashcam API key (optional, uses DASHCAM_API_KEY env var if not provided)
17
+ * @returns Promise that resolves when authenticated
18
+ */
19
+ auth(apiKey?: string): Promise<void>;
20
+
21
+ /**
22
+ * Add a log entry to Dashcam
23
+ * @param config - Log configuration
24
+ */
25
+ addLog(config: LogConfig): Promise<void>;
26
+
27
+ /**
28
+ * Add a file log to Dashcam
29
+ * @param path - Path to file to log
30
+ * @param name - Name/description for the log entry
31
+ */
32
+ addFileLog(path: string, name: string): Promise<void>;
33
+
34
+ /**
35
+ * Add an application log to Dashcam
36
+ * @param application - Application name to track
37
+ * @param name - Name/description for the log entry
38
+ */
39
+ addApplicationLog(application: string, name: string): Promise<void>;
40
+
41
+ /**
42
+ * Start recording
43
+ * @returns Promise that resolves when recording starts
44
+ */
45
+ start(): Promise<void>;
46
+
47
+ /**
48
+ * Stop recording and get replay URL
49
+ * @returns Promise that resolves to the replay URL (or null if not recording)
50
+ */
51
+ stop(): Promise<string | null>;
52
+
53
+ /**
54
+ * Check if currently recording
55
+ * @returns true if recording, false otherwise
56
+ */
57
+ isRecording(): boolean;
58
+ }
59
+
60
+ export interface DashcamOptions {
61
+ /**
62
+ * Dashcam API key (defaults to DASHCAM_API_KEY env var)
63
+ */
64
+ apiKey?: string;
65
+ }
66
+
67
+ export interface LogConfig {
68
+ /**
69
+ * Type of log entry
70
+ */
71
+ type: 'file' | 'application';
72
+
73
+ /**
74
+ * Path to file (for file logs)
75
+ */
76
+ path?: string;
77
+
78
+ /**
79
+ * Application name (for application logs)
80
+ */
81
+ application?: string;
82
+
83
+ /**
84
+ * Name/description for the log entry
85
+ */
86
+ name: string;
87
+ }
88
+
89
+ /**
90
+ * TestDriver SDK class
91
+ * Re-exported from main module for convenience
92
+ */
93
+ export class TestDriver {
94
+ constructor(apiKey: string, options?: TestDriverOptions);
95
+
96
+ auth(): Promise<void>;
97
+ connect(options?: ConnectOptions): Promise<any>;
98
+ disconnect(): Promise<void>;
99
+
100
+ find(query: string): Promise<any>;
101
+ findAll(query: string): Promise<any[]>;
102
+ click(target: string): Promise<void>;
103
+ type(target: string, text: string): Promise<void>;
104
+ exec(shell: string, command: string, timeout?: number, ignoreError?: boolean): Promise<string>;
105
+ focusApplication(appName: string): Promise<void>;
106
+
107
+ // Add other TestDriver methods as needed
108
+ }
109
+
110
+ export interface TestDriverOptions {
111
+ /**
112
+ * API endpoint URL
113
+ */
114
+ apiRoot?: string;
115
+
116
+ /**
117
+ * Target OS: 'linux', 'mac', or 'windows'
118
+ */
119
+ os?: 'linux' | 'mac' | 'windows';
120
+
121
+ /**
122
+ * Create new sandbox
123
+ */
124
+ newSandbox?: boolean;
125
+
126
+ /**
127
+ * Screen resolution
128
+ */
129
+ resolution?: string;
130
+
131
+ /**
132
+ * Enable analytics
133
+ */
134
+ analytics?: boolean;
135
+
136
+ /**
137
+ * Cache configuration
138
+ * Set to false to disable caching entirely.
139
+ * Set to an object to configure thresholds.
140
+ * @example { cache: { enabled: true, thresholds: { find: { screen: 0.05, element: 0.8 }, assert: 0.05 } } }
141
+ */
142
+ cache?: boolean | {
143
+ enabled?: boolean;
144
+ thresholds?: {
145
+ /** Thresholds for find operations */
146
+ find?: {
147
+ /** Pixel diff threshold for screen comparison (0-1, default 0.05 = 5% diff allowed) */
148
+ screen?: number;
149
+ /** OpenCV template match threshold for element matching (0-1, default 0.8 = 80% correlation) */
150
+ element?: number;
151
+ };
152
+ /** Pixel diff threshold for assert operations (0-1, default 0.05 = 5% diff allowed) */
153
+ assert?: number;
154
+ };
155
+ };
156
+
157
+ /**
158
+ * @deprecated Use cache.thresholds instead
159
+ * Cache thresholds for find operations
160
+ */
161
+ cacheThresholds?: {
162
+ find?: number;
163
+ findAll?: number;
164
+ };
165
+ }
166
+
167
+ export interface ConnectOptions {
168
+ /**
169
+ * Create new sandbox instance
170
+ */
171
+ new?: boolean;
172
+ }