tabwright 1.0.0

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 (599) hide show
  1. package/LICENSE +21 -0
  2. package/bin.js +4 -0
  3. package/dist/a11y-client.js +204 -0
  4. package/dist/agent-skills/tabwright/SKILL.md +119 -0
  5. package/dist/aria-snapshot.d.ts +262 -0
  6. package/dist/aria-snapshot.d.ts.map +1 -0
  7. package/dist/aria-snapshot.js +1256 -0
  8. package/dist/aria-snapshot.js.map +1 -0
  9. package/dist/aria-snapshot.test.d.ts +2 -0
  10. package/dist/aria-snapshot.test.d.ts.map +1 -0
  11. package/dist/aria-snapshot.test.js +270 -0
  12. package/dist/aria-snapshot.test.js.map +1 -0
  13. package/dist/aria-snapshot.unit.test.d.ts +2 -0
  14. package/dist/aria-snapshot.unit.test.d.ts.map +1 -0
  15. package/dist/aria-snapshot.unit.test.js +542 -0
  16. package/dist/aria-snapshot.unit.test.js.map +1 -0
  17. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +5 -0
  18. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +1 -0
  19. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +5 -0
  20. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +1 -0
  21. package/dist/bippy.js +894 -0
  22. package/dist/browser-config.d.ts +13 -0
  23. package/dist/browser-config.d.ts.map +1 -0
  24. package/dist/browser-config.js +126 -0
  25. package/dist/browser-config.js.map +1 -0
  26. package/dist/browser-install.d.ts +16 -0
  27. package/dist/browser-install.d.ts.map +1 -0
  28. package/dist/browser-install.js +238 -0
  29. package/dist/browser-install.js.map +1 -0
  30. package/dist/browser-launch.d.ts +17 -0
  31. package/dist/browser-launch.d.ts.map +1 -0
  32. package/dist/browser-launch.js +44 -0
  33. package/dist/browser-launch.js.map +1 -0
  34. package/dist/builtin-capabilities.d.ts +28 -0
  35. package/dist/builtin-capabilities.d.ts.map +1 -0
  36. package/dist/builtin-capabilities.js +2673 -0
  37. package/dist/builtin-capabilities.js.map +1 -0
  38. package/dist/capability-agent-skill.d.ts +60 -0
  39. package/dist/capability-agent-skill.d.ts.map +1 -0
  40. package/dist/capability-agent-skill.js +225 -0
  41. package/dist/capability-agent-skill.js.map +1 -0
  42. package/dist/capability-auth-state.d.ts +34 -0
  43. package/dist/capability-auth-state.d.ts.map +1 -0
  44. package/dist/capability-auth-state.js +157 -0
  45. package/dist/capability-auth-state.js.map +1 -0
  46. package/dist/capability-auth.d.ts +36 -0
  47. package/dist/capability-auth.d.ts.map +1 -0
  48. package/dist/capability-auth.js +198 -0
  49. package/dist/capability-auth.js.map +1 -0
  50. package/dist/capability-cli-confirmation.test.d.ts +2 -0
  51. package/dist/capability-cli-confirmation.test.d.ts.map +1 -0
  52. package/dist/capability-cli-confirmation.test.js +79 -0
  53. package/dist/capability-cli-confirmation.test.js.map +1 -0
  54. package/dist/capability-options.d.ts +20 -0
  55. package/dist/capability-options.d.ts.map +1 -0
  56. package/dist/capability-options.js +31 -0
  57. package/dist/capability-options.js.map +1 -0
  58. package/dist/capability-options.test.d.ts +2 -0
  59. package/dist/capability-options.test.d.ts.map +1 -0
  60. package/dist/capability-options.test.js +81 -0
  61. package/dist/capability-options.test.js.map +1 -0
  62. package/dist/capability-package.d.ts +27 -0
  63. package/dist/capability-package.d.ts.map +1 -0
  64. package/dist/capability-package.js +444 -0
  65. package/dist/capability-package.js.map +1 -0
  66. package/dist/capability-package.test.d.ts +2 -0
  67. package/dist/capability-package.test.d.ts.map +1 -0
  68. package/dist/capability-package.test.js +179 -0
  69. package/dist/capability-package.test.js.map +1 -0
  70. package/dist/capability-registry.d.ts +262 -0
  71. package/dist/capability-registry.d.ts.map +1 -0
  72. package/dist/capability-registry.js +931 -0
  73. package/dist/capability-registry.js.map +1 -0
  74. package/dist/capability-registry.test.d.ts +2 -0
  75. package/dist/capability-registry.test.d.ts.map +1 -0
  76. package/dist/capability-registry.test.js +1683 -0
  77. package/dist/capability-registry.test.js.map +1 -0
  78. package/dist/capability-runner.d.ts +99 -0
  79. package/dist/capability-runner.d.ts.map +1 -0
  80. package/dist/capability-runner.js +608 -0
  81. package/dist/capability-runner.js.map +1 -0
  82. package/dist/capability-search-relevance.test.d.ts +2 -0
  83. package/dist/capability-search-relevance.test.d.ts.map +1 -0
  84. package/dist/capability-search-relevance.test.js +67 -0
  85. package/dist/capability-search-relevance.test.js.map +1 -0
  86. package/dist/capability-studio.d.ts +11 -0
  87. package/dist/capability-studio.d.ts.map +1 -0
  88. package/dist/capability-studio.js +593 -0
  89. package/dist/capability-studio.js.map +1 -0
  90. package/dist/capability-studio.test.d.ts +2 -0
  91. package/dist/capability-studio.test.d.ts.map +1 -0
  92. package/dist/capability-studio.test.js +41 -0
  93. package/dist/capability-studio.test.js.map +1 -0
  94. package/dist/cdp-log.d.ts +19 -0
  95. package/dist/cdp-log.d.ts.map +1 -0
  96. package/dist/cdp-log.js +83 -0
  97. package/dist/cdp-log.js.map +1 -0
  98. package/dist/cdp-log.test.d.ts +2 -0
  99. package/dist/cdp-log.test.d.ts.map +1 -0
  100. package/dist/cdp-log.test.js +109 -0
  101. package/dist/cdp-log.test.js.map +1 -0
  102. package/dist/cdp-relay.d.ts +19 -0
  103. package/dist/cdp-relay.d.ts.map +1 -0
  104. package/dist/cdp-relay.js +2375 -0
  105. package/dist/cdp-relay.js.map +1 -0
  106. package/dist/cdp-session.d.ts +37 -0
  107. package/dist/cdp-session.d.ts.map +1 -0
  108. package/dist/cdp-session.js +36 -0
  109. package/dist/cdp-session.js.map +1 -0
  110. package/dist/cdp-types.d.ts +65 -0
  111. package/dist/cdp-types.d.ts.map +1 -0
  112. package/dist/cdp-types.js +91 -0
  113. package/dist/cdp-types.js.map +1 -0
  114. package/dist/channel-owner-inspect.test.d.ts +2 -0
  115. package/dist/channel-owner-inspect.test.d.ts.map +1 -0
  116. package/dist/channel-owner-inspect.test.js +75 -0
  117. package/dist/channel-owner-inspect.test.js.map +1 -0
  118. package/dist/chrome-discovery.d.ts +65 -0
  119. package/dist/chrome-discovery.d.ts.map +1 -0
  120. package/dist/chrome-discovery.js +173 -0
  121. package/dist/chrome-discovery.js.map +1 -0
  122. package/dist/chrome-discovery.test.d.ts +2 -0
  123. package/dist/chrome-discovery.test.d.ts.map +1 -0
  124. package/dist/chrome-discovery.test.js +41 -0
  125. package/dist/chrome-discovery.test.js.map +1 -0
  126. package/dist/clean-html.d.ts +11 -0
  127. package/dist/clean-html.d.ts.map +1 -0
  128. package/dist/clean-html.js +106 -0
  129. package/dist/clean-html.js.map +1 -0
  130. package/dist/cli-help.test.d.ts +2 -0
  131. package/dist/cli-help.test.d.ts.map +1 -0
  132. package/dist/cli-help.test.js +98 -0
  133. package/dist/cli-help.test.js.map +1 -0
  134. package/dist/cli.d.ts +3 -0
  135. package/dist/cli.d.ts.map +1 -0
  136. package/dist/cli.js +2709 -0
  137. package/dist/cli.js.map +1 -0
  138. package/dist/cloud-client.d.ts +56 -0
  139. package/dist/cloud-client.d.ts.map +1 -0
  140. package/dist/cloud-client.js +127 -0
  141. package/dist/cloud-client.js.map +1 -0
  142. package/dist/create-logger.d.ts +9 -0
  143. package/dist/create-logger.d.ts.map +1 -0
  144. package/dist/create-logger.js +27 -0
  145. package/dist/create-logger.js.map +1 -0
  146. package/dist/debugger-api.md +458 -0
  147. package/dist/debugger-examples-types.d.ts +24 -0
  148. package/dist/debugger-examples-types.d.ts.map +1 -0
  149. package/dist/debugger-examples-types.js +2 -0
  150. package/dist/debugger-examples-types.js.map +1 -0
  151. package/dist/debugger-examples.d.ts +6 -0
  152. package/dist/debugger-examples.d.ts.map +1 -0
  153. package/dist/debugger-examples.js +53 -0
  154. package/dist/debugger-examples.js.map +1 -0
  155. package/dist/debugger.d.ts +381 -0
  156. package/dist/debugger.d.ts.map +1 -0
  157. package/dist/debugger.js +630 -0
  158. package/dist/debugger.js.map +1 -0
  159. package/dist/diff-utils.d.ts +20 -0
  160. package/dist/diff-utils.d.ts.map +1 -0
  161. package/dist/diff-utils.js +50 -0
  162. package/dist/diff-utils.js.map +1 -0
  163. package/dist/diff-utils.test.d.ts +2 -0
  164. package/dist/diff-utils.test.d.ts.map +1 -0
  165. package/dist/diff-utils.test.js +137 -0
  166. package/dist/diff-utils.test.js.map +1 -0
  167. package/dist/doctor.d.ts +44 -0
  168. package/dist/doctor.d.ts.map +1 -0
  169. package/dist/doctor.js +246 -0
  170. package/dist/doctor.js.map +1 -0
  171. package/dist/doctor.test.d.ts +2 -0
  172. package/dist/doctor.test.d.ts.map +1 -0
  173. package/dist/doctor.test.js +208 -0
  174. package/dist/doctor.test.js.map +1 -0
  175. package/dist/editor-api.md +374 -0
  176. package/dist/editor-examples.d.ts +11 -0
  177. package/dist/editor-examples.d.ts.map +1 -0
  178. package/dist/editor-examples.js +124 -0
  179. package/dist/editor-examples.js.map +1 -0
  180. package/dist/editor.d.ts +203 -0
  181. package/dist/editor.d.ts.map +1 -0
  182. package/dist/editor.js +363 -0
  183. package/dist/editor.js.map +1 -0
  184. package/dist/env-compat.d.ts +2 -0
  185. package/dist/env-compat.d.ts.map +1 -0
  186. package/dist/env-compat.js +27 -0
  187. package/dist/env-compat.js.map +1 -0
  188. package/dist/executor.d.ts +229 -0
  189. package/dist/executor.d.ts.map +1 -0
  190. package/dist/executor.js +1465 -0
  191. package/dist/executor.js.map +1 -0
  192. package/dist/executor.unit.test.d.ts +2 -0
  193. package/dist/executor.unit.test.d.ts.map +1 -0
  194. package/dist/executor.unit.test.js +143 -0
  195. package/dist/executor.unit.test.js.map +1 -0
  196. package/dist/extension/_locales/en/messages.json +278 -0
  197. package/dist/extension/_locales/zh_CN/messages.json +256 -0
  198. package/dist/extension/assets/options-CYH96Lcx.css +1349 -0
  199. package/dist/extension/background.js +3994 -0
  200. package/dist/extension/icons/SolarCursorSquareBold.png +0 -0
  201. package/dist/extension/icons/icon-black-128.png +0 -0
  202. package/dist/extension/icons/icon-black-16.png +0 -0
  203. package/dist/extension/icons/icon-black-32.png +0 -0
  204. package/dist/extension/icons/icon-black-48.png +0 -0
  205. package/dist/extension/icons/icon-gray-128.png +0 -0
  206. package/dist/extension/icons/icon-gray-16.png +0 -0
  207. package/dist/extension/icons/icon-gray-32.png +0 -0
  208. package/dist/extension/icons/icon-gray-48.png +0 -0
  209. package/dist/extension/icons/icon-green-128.png +0 -0
  210. package/dist/extension/icons/icon-green-16.png +0 -0
  211. package/dist/extension/icons/icon-green-32.png +0 -0
  212. package/dist/extension/icons/icon-green-48.png +0 -0
  213. package/dist/extension/icons/tabwright-icon-black.png +0 -0
  214. package/dist/extension/icons/tabwright-icon-gray-disabled.png +0 -0
  215. package/dist/extension/icons/tabwright-icon-gray-disabled.svg +1 -0
  216. package/dist/extension/manifest.json +54 -0
  217. package/dist/extension/options.js +16145 -0
  218. package/dist/extension/rrweb-recorder.js +12952 -0
  219. package/dist/extension/src/options.html +165 -0
  220. package/dist/extension/src/prism-bash.min.js +1 -0
  221. package/dist/extension/src/prism.min.js +1 -0
  222. package/dist/extension/src/welcome.html +396 -0
  223. package/dist/extension-connection.test.d.ts +2 -0
  224. package/dist/extension-connection.test.d.ts.map +1 -0
  225. package/dist/extension-connection.test.js +708 -0
  226. package/dist/extension-connection.test.js.map +1 -0
  227. package/dist/extension-icon-warning.test.d.ts +2 -0
  228. package/dist/extension-icon-warning.test.d.ts.map +1 -0
  229. package/dist/extension-icon-warning.test.js +33 -0
  230. package/dist/extension-icon-warning.test.js.map +1 -0
  231. package/dist/ghost-browser.d.ts +62 -0
  232. package/dist/ghost-browser.d.ts.map +1 -0
  233. package/dist/ghost-browser.js +107 -0
  234. package/dist/ghost-browser.js.map +1 -0
  235. package/dist/ghost-cursor-client.js +374 -0
  236. package/dist/ghost-cursor-controller.d.ts +46 -0
  237. package/dist/ghost-cursor-controller.d.ts.map +1 -0
  238. package/dist/ghost-cursor-controller.js +98 -0
  239. package/dist/ghost-cursor-controller.js.map +1 -0
  240. package/dist/ghost-cursor.d.ts +27 -0
  241. package/dist/ghost-cursor.d.ts.map +1 -0
  242. package/dist/ghost-cursor.js +79 -0
  243. package/dist/ghost-cursor.js.map +1 -0
  244. package/dist/htmlrewrite.d.ts +8 -0
  245. package/dist/htmlrewrite.d.ts.map +1 -0
  246. package/dist/htmlrewrite.js +339 -0
  247. package/dist/htmlrewrite.js.map +1 -0
  248. package/dist/htmlrewrite.test.d.ts +2 -0
  249. package/dist/htmlrewrite.test.d.ts.map +1 -0
  250. package/dist/htmlrewrite.test.js +7975 -0
  251. package/dist/htmlrewrite.test.js.map +1 -0
  252. package/dist/index.d.ts +20 -0
  253. package/dist/index.d.ts.map +1 -0
  254. package/dist/index.js +11 -0
  255. package/dist/index.js.map +1 -0
  256. package/dist/kill-port.d.ts +29 -0
  257. package/dist/kill-port.d.ts.map +1 -0
  258. package/dist/kill-port.js +190 -0
  259. package/dist/kill-port.js.map +1 -0
  260. package/dist/kitty-graphics.d.ts +22 -0
  261. package/dist/kitty-graphics.d.ts.map +1 -0
  262. package/dist/kitty-graphics.js +71 -0
  263. package/dist/kitty-graphics.js.map +1 -0
  264. package/dist/kitty-graphics.test.d.ts +2 -0
  265. package/dist/kitty-graphics.test.d.ts.map +1 -0
  266. package/dist/kitty-graphics.test.js +125 -0
  267. package/dist/kitty-graphics.test.js.map +1 -0
  268. package/dist/locator-selector.test.d.ts +2 -0
  269. package/dist/locator-selector.test.d.ts.map +1 -0
  270. package/dist/locator-selector.test.js +98 -0
  271. package/dist/locator-selector.test.js.map +1 -0
  272. package/dist/mcp-client.d.ts +20 -0
  273. package/dist/mcp-client.d.ts.map +1 -0
  274. package/dist/mcp-client.js +56 -0
  275. package/dist/mcp-client.js.map +1 -0
  276. package/dist/mcp.d.ts +6 -0
  277. package/dist/mcp.d.ts.map +1 -0
  278. package/dist/mcp.js +511 -0
  279. package/dist/mcp.js.map +1 -0
  280. package/dist/on-mouse-action.test.d.ts +2 -0
  281. package/dist/on-mouse-action.test.d.ts.map +1 -0
  282. package/dist/on-mouse-action.test.js +180 -0
  283. package/dist/on-mouse-action.test.js.map +1 -0
  284. package/dist/options-page.test.d.ts +2 -0
  285. package/dist/options-page.test.d.ts.map +1 -0
  286. package/dist/options-page.test.js +773 -0
  287. package/dist/options-page.test.js.map +1 -0
  288. package/dist/package-paths.d.ts +4 -0
  289. package/dist/package-paths.d.ts.map +1 -0
  290. package/dist/package-paths.js +30 -0
  291. package/dist/package-paths.js.map +1 -0
  292. package/dist/page-markdown.d.ts +40 -0
  293. package/dist/page-markdown.d.ts.map +1 -0
  294. package/dist/page-markdown.js +181 -0
  295. package/dist/page-markdown.js.map +1 -0
  296. package/dist/performance-examples.d.ts +5 -0
  297. package/dist/performance-examples.d.ts.map +1 -0
  298. package/dist/performance-examples.js +112 -0
  299. package/dist/performance-examples.js.map +1 -0
  300. package/dist/performance-profiling.md +417 -0
  301. package/dist/playwright-import.d.ts +19 -0
  302. package/dist/playwright-import.d.ts.map +1 -0
  303. package/dist/playwright-import.js +40 -0
  304. package/dist/playwright-import.js.map +1 -0
  305. package/dist/popup-relocation.test.d.ts +7 -0
  306. package/dist/popup-relocation.test.d.ts.map +1 -0
  307. package/dist/popup-relocation.test.js +139 -0
  308. package/dist/popup-relocation.test.js.map +1 -0
  309. package/dist/product-paths.d.ts +9 -0
  310. package/dist/product-paths.d.ts.map +1 -0
  311. package/dist/product-paths.js +24 -0
  312. package/dist/product-paths.js.map +1 -0
  313. package/dist/product-paths.test.d.ts +2 -0
  314. package/dist/product-paths.test.d.ts.map +1 -0
  315. package/dist/product-paths.test.js +39 -0
  316. package/dist/product-paths.test.js.map +1 -0
  317. package/dist/prompt.md +1004 -0
  318. package/dist/protocol.d.ts +248 -0
  319. package/dist/protocol.d.ts.map +1 -0
  320. package/dist/protocol.js +59 -0
  321. package/dist/protocol.js.map +1 -0
  322. package/dist/protocol.test.d.ts +2 -0
  323. package/dist/protocol.test.d.ts.map +1 -0
  324. package/dist/protocol.test.js +31 -0
  325. package/dist/protocol.test.js.map +1 -0
  326. package/dist/react-source.d.ts +57 -0
  327. package/dist/react-source.d.ts.map +1 -0
  328. package/dist/react-source.js +254 -0
  329. package/dist/react-source.js.map +1 -0
  330. package/dist/readability.js +1674 -0
  331. package/dist/relay-client.d.ts +104 -0
  332. package/dist/relay-client.d.ts.map +1 -0
  333. package/dist/relay-client.js +426 -0
  334. package/dist/relay-client.js.map +1 -0
  335. package/dist/relay-client.test.d.ts +2 -0
  336. package/dist/relay-client.test.d.ts.map +1 -0
  337. package/dist/relay-client.test.js +305 -0
  338. package/dist/relay-client.test.js.map +1 -0
  339. package/dist/relay-core.test.d.ts +2 -0
  340. package/dist/relay-core.test.d.ts.map +1 -0
  341. package/dist/relay-core.test.js +1427 -0
  342. package/dist/relay-core.test.js.map +1 -0
  343. package/dist/relay-navigation.test.d.ts +2 -0
  344. package/dist/relay-navigation.test.d.ts.map +1 -0
  345. package/dist/relay-navigation.test.js +661 -0
  346. package/dist/relay-navigation.test.js.map +1 -0
  347. package/dist/relay-session.test.d.ts +2 -0
  348. package/dist/relay-session.test.d.ts.map +1 -0
  349. package/dist/relay-session.test.js +1121 -0
  350. package/dist/relay-session.test.js.map +1 -0
  351. package/dist/relay-state.d.ts +178 -0
  352. package/dist/relay-state.d.ts.map +1 -0
  353. package/dist/relay-state.js +346 -0
  354. package/dist/relay-state.js.map +1 -0
  355. package/dist/relay-state.test.d.ts +2 -0
  356. package/dist/relay-state.test.d.ts.map +1 -0
  357. package/dist/relay-state.test.js +594 -0
  358. package/dist/relay-state.test.js.map +1 -0
  359. package/dist/replay-ai-index.d.ts +99 -0
  360. package/dist/replay-ai-index.d.ts.map +1 -0
  361. package/dist/replay-ai-index.js +677 -0
  362. package/dist/replay-ai-index.js.map +1 -0
  363. package/dist/replay-ai-index.test.d.ts +2 -0
  364. package/dist/replay-ai-index.test.d.ts.map +1 -0
  365. package/dist/replay-ai-index.test.js +255 -0
  366. package/dist/replay-ai-index.test.js.map +1 -0
  367. package/dist/replay-cli.test.d.ts +2 -0
  368. package/dist/replay-cli.test.d.ts.map +1 -0
  369. package/dist/replay-cli.test.js +275 -0
  370. package/dist/replay-cli.test.js.map +1 -0
  371. package/dist/replay-eval.d.ts +44 -0
  372. package/dist/replay-eval.d.ts.map +1 -0
  373. package/dist/replay-eval.js +902 -0
  374. package/dist/replay-eval.js.map +1 -0
  375. package/dist/replay-handoff.d.ts +29 -0
  376. package/dist/replay-handoff.d.ts.map +1 -0
  377. package/dist/replay-handoff.js +83 -0
  378. package/dist/replay-handoff.js.map +1 -0
  379. package/dist/replay-handoff.test.d.ts +2 -0
  380. package/dist/replay-handoff.test.d.ts.map +1 -0
  381. package/dist/replay-handoff.test.js +123 -0
  382. package/dist/replay-handoff.test.js.map +1 -0
  383. package/dist/replay-workflow-compiler.d.ts +44 -0
  384. package/dist/replay-workflow-compiler.d.ts.map +1 -0
  385. package/dist/replay-workflow-compiler.js +601 -0
  386. package/dist/replay-workflow-compiler.js.map +1 -0
  387. package/dist/replay-workflow-compiler.test.d.ts +2 -0
  388. package/dist/replay-workflow-compiler.test.d.ts.map +1 -0
  389. package/dist/replay-workflow-compiler.test.js +223 -0
  390. package/dist/replay-workflow-compiler.test.js.map +1 -0
  391. package/dist/rrweb-recording-relay.d.ts +56 -0
  392. package/dist/rrweb-recording-relay.d.ts.map +1 -0
  393. package/dist/rrweb-recording-relay.js +299 -0
  394. package/dist/rrweb-recording-relay.js.map +1 -0
  395. package/dist/rrweb-recording-relay.test.d.ts +2 -0
  396. package/dist/rrweb-recording-relay.test.d.ts.map +1 -0
  397. package/dist/rrweb-recording-relay.test.js +85 -0
  398. package/dist/rrweb-recording-relay.test.js.map +1 -0
  399. package/dist/rrweb-recording.d.ts +104 -0
  400. package/dist/rrweb-recording.d.ts.map +1 -0
  401. package/dist/rrweb-recording.js +180 -0
  402. package/dist/rrweb-recording.js.map +1 -0
  403. package/dist/scoped-fs.d.ts +95 -0
  404. package/dist/scoped-fs.d.ts.map +1 -0
  405. package/dist/scoped-fs.js +358 -0
  406. package/dist/scoped-fs.js.map +1 -0
  407. package/dist/scoped-fs.test.d.ts +5 -0
  408. package/dist/scoped-fs.test.d.ts.map +1 -0
  409. package/dist/scoped-fs.test.js +73 -0
  410. package/dist/scoped-fs.test.js.map +1 -0
  411. package/dist/selector-generator.js +7378 -0
  412. package/dist/skill-stub.test.d.ts +2 -0
  413. package/dist/skill-stub.test.d.ts.map +1 -0
  414. package/dist/skill-stub.test.js +22 -0
  415. package/dist/skill-stub.test.js.map +1 -0
  416. package/dist/snapshot-tools.test.d.ts +2 -0
  417. package/dist/snapshot-tools.test.d.ts.map +1 -0
  418. package/dist/snapshot-tools.test.js +856 -0
  419. package/dist/snapshot-tools.test.js.map +1 -0
  420. package/dist/start-relay-server.d.ts +6 -0
  421. package/dist/start-relay-server.d.ts.map +1 -0
  422. package/dist/start-relay-server.js +57 -0
  423. package/dist/start-relay-server.js.map +1 -0
  424. package/dist/styles-api.md +124 -0
  425. package/dist/styles-examples.d.ts +8 -0
  426. package/dist/styles-examples.d.ts.map +1 -0
  427. package/dist/styles-examples.js +64 -0
  428. package/dist/styles-examples.js.map +1 -0
  429. package/dist/styles.d.ts +27 -0
  430. package/dist/styles.d.ts.map +1 -0
  431. package/dist/styles.js +231 -0
  432. package/dist/styles.js.map +1 -0
  433. package/dist/tabwright-agent-skill.d.ts +30 -0
  434. package/dist/tabwright-agent-skill.d.ts.map +1 -0
  435. package/dist/tabwright-agent-skill.js +74 -0
  436. package/dist/tabwright-agent-skill.js.map +1 -0
  437. package/dist/tabwright-agent-skill.test.d.ts +2 -0
  438. package/dist/tabwright-agent-skill.test.d.ts.map +1 -0
  439. package/dist/tabwright-agent-skill.test.js +51 -0
  440. package/dist/tabwright-agent-skill.test.js.map +1 -0
  441. package/dist/test-declarations.d.ts +13 -0
  442. package/dist/test-declarations.d.ts.map +1 -0
  443. package/dist/test-declarations.js +2 -0
  444. package/dist/test-declarations.js.map +1 -0
  445. package/dist/test-utils.d.ts +69 -0
  446. package/dist/test-utils.d.ts.map +1 -0
  447. package/dist/test-utils.js +448 -0
  448. package/dist/test-utils.js.map +1 -0
  449. package/dist/test-utils.test.d.ts +2 -0
  450. package/dist/test-utils.test.d.ts.map +1 -0
  451. package/dist/test-utils.test.js +55 -0
  452. package/dist/test-utils.test.js.map +1 -0
  453. package/dist/utils.d.ts +26 -0
  454. package/dist/utils.d.ts.map +1 -0
  455. package/dist/utils.js +60 -0
  456. package/dist/utils.js.map +1 -0
  457. package/dist/wait-for-page-load.d.ts +16 -0
  458. package/dist/wait-for-page-load.d.ts.map +1 -0
  459. package/dist/wait-for-page-load.js +146 -0
  460. package/dist/wait-for-page-load.js.map +1 -0
  461. package/dist/workflow-capability.d.ts +72 -0
  462. package/dist/workflow-capability.d.ts.map +1 -0
  463. package/dist/workflow-capability.js +316 -0
  464. package/dist/workflow-capability.js.map +1 -0
  465. package/dist/workflow-script-flow.test.d.ts +2 -0
  466. package/dist/workflow-script-flow.test.d.ts.map +1 -0
  467. package/dist/workflow-script-flow.test.js +285 -0
  468. package/dist/workflow-script-flow.test.js.map +1 -0
  469. package/package.json +80 -0
  470. package/src/__snapshots__/x.com.processed.html +1003 -0
  471. package/src/__snapshots__/x.com.processed.withStyles.html +2720 -0
  472. package/src/a11y-client.ts +241 -0
  473. package/src/agent-skills/conan-config/SKILL.md +141 -0
  474. package/src/agent-skills/conan-config/agents/openai.yaml +4 -0
  475. package/src/aria-snapshot.test.ts +311 -0
  476. package/src/aria-snapshot.ts +1681 -0
  477. package/src/aria-snapshot.unit.test.ts +615 -0
  478. package/src/aria-snapshots/github-interactive.txt +193 -0
  479. package/src/aria-snapshots/github-raw.txt +309 -0
  480. package/src/aria-snapshots/hackernews-interactive.txt +477 -0
  481. package/src/aria-snapshots/hackernews-raw.txt +428 -0
  482. package/src/aria-snapshots/prosemirror-interactive.txt +66 -0
  483. package/src/aria-snapshots/prosemirror-raw.txt +145 -0
  484. package/src/assets/aria-labels-hacker-news.png +0 -0
  485. package/src/assets/aria-labels-old-reddit.png +0 -0
  486. package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +5 -0
  487. package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +18 -0
  488. package/src/assets/x.com.html +32946 -0
  489. package/src/browser-config.ts +179 -0
  490. package/src/browser-install.ts +284 -0
  491. package/src/browser-launch.ts +75 -0
  492. package/src/builtin-capabilities.ts +2765 -0
  493. package/src/capability-agent-skill.ts +305 -0
  494. package/src/capability-auth-state.ts +197 -0
  495. package/src/capability-auth.ts +249 -0
  496. package/src/capability-cli-confirmation.test.ts +90 -0
  497. package/src/capability-options.test.ts +89 -0
  498. package/src/capability-options.ts +57 -0
  499. package/src/capability-package.test.ts +204 -0
  500. package/src/capability-package.ts +555 -0
  501. package/src/capability-registry.test.ts +1905 -0
  502. package/src/capability-registry.ts +1234 -0
  503. package/src/capability-runner.ts +801 -0
  504. package/src/capability-search-relevance.test.ts +73 -0
  505. package/src/capability-studio.test.ts +45 -0
  506. package/src/capability-studio.ts +641 -0
  507. package/src/cdp-log.test.ts +131 -0
  508. package/src/cdp-log.ts +112 -0
  509. package/src/cdp-relay.ts +2944 -0
  510. package/src/cdp-session.ts +77 -0
  511. package/src/cdp-types.ts +158 -0
  512. package/src/channel-owner-inspect.test.ts +94 -0
  513. package/src/chrome-discovery.test.ts +50 -0
  514. package/src/chrome-discovery.ts +220 -0
  515. package/src/clean-html.ts +141 -0
  516. package/src/cli-help.test.ts +123 -0
  517. package/src/cli.ts +3181 -0
  518. package/src/cloud-client.ts +179 -0
  519. package/src/create-logger.ts +38 -0
  520. package/src/debugger-examples-types.ts +16 -0
  521. package/src/debugger-examples.ts +66 -0
  522. package/src/debugger.ts +708 -0
  523. package/src/diff-utils.test.ts +152 -0
  524. package/src/diff-utils.ts +70 -0
  525. package/src/doctor.test.ts +244 -0
  526. package/src/doctor.ts +321 -0
  527. package/src/editor-examples.ts +158 -0
  528. package/src/editor.ts +426 -0
  529. package/src/env-compat.ts +28 -0
  530. package/src/executor.ts +1824 -0
  531. package/src/executor.unit.test.ts +168 -0
  532. package/src/extension-connection.test.ts +855 -0
  533. package/src/extension-icon-warning.test.ts +45 -0
  534. package/src/ghost-browser.ts +149 -0
  535. package/src/ghost-cursor-client.ts +494 -0
  536. package/src/ghost-cursor-controller.ts +129 -0
  537. package/src/ghost-cursor.ts +123 -0
  538. package/src/htmlrewrite.test.ts +8008 -0
  539. package/src/htmlrewrite.ts +390 -0
  540. package/src/index.ts +53 -0
  541. package/src/kill-port.ts +219 -0
  542. package/src/kitty-graphics.test.ts +139 -0
  543. package/src/kitty-graphics.ts +79 -0
  544. package/src/locator-selector.test.ts +119 -0
  545. package/src/mcp-client.ts +78 -0
  546. package/src/mcp.ts +629 -0
  547. package/src/on-mouse-action.test.ts +226 -0
  548. package/src/options-page.test.ts +839 -0
  549. package/src/package-paths.ts +38 -0
  550. package/src/page-markdown.ts +240 -0
  551. package/src/performance-examples.ts +186 -0
  552. package/src/playwright-import.ts +59 -0
  553. package/src/popup-relocation.test.ts +163 -0
  554. package/src/product-paths.test.ts +47 -0
  555. package/src/product-paths.ts +28 -0
  556. package/src/protocol.test.ts +52 -0
  557. package/src/protocol.ts +351 -0
  558. package/src/react-source.ts +379 -0
  559. package/src/relay-client.test.ts +375 -0
  560. package/src/relay-client.ts +572 -0
  561. package/src/relay-core.test.ts +1603 -0
  562. package/src/relay-navigation.test.ts +790 -0
  563. package/src/relay-session.test.ts +1375 -0
  564. package/src/relay-state.test.ts +729 -0
  565. package/src/relay-state.ts +561 -0
  566. package/src/replay-ai-index.test.ts +278 -0
  567. package/src/replay-ai-index.ts +872 -0
  568. package/src/replay-cli.test.ts +325 -0
  569. package/src/replay-eval.ts +1039 -0
  570. package/src/replay-handoff.test.ts +163 -0
  571. package/src/replay-handoff.ts +109 -0
  572. package/src/replay-workflow-compiler.test.ts +250 -0
  573. package/src/replay-workflow-compiler.ts +700 -0
  574. package/src/resource.md +409 -0
  575. package/src/rrweb-recording-relay.test.ts +99 -0
  576. package/src/rrweb-recording-relay.ts +400 -0
  577. package/src/rrweb-recording.ts +322 -0
  578. package/src/scoped-fs.test.ts +80 -0
  579. package/src/scoped-fs.ts +420 -0
  580. package/src/skill-stub.test.ts +24 -0
  581. package/src/skill.md +1413 -0
  582. package/src/snapshot-tools.test.ts +1017 -0
  583. package/src/snapshots/hacker-news-accessibility-full.md +81 -0
  584. package/src/snapshots/hacker-news-accessibility-interactive.md +43 -0
  585. package/src/snapshots/page-markdown-output.txt +16 -0
  586. package/src/snapshots/shadcn-ui-accessibility-full.md +201 -0
  587. package/src/snapshots/shadcn-ui-accessibility-interactive.md +109 -0
  588. package/src/start-relay-server.ts +69 -0
  589. package/src/styles-examples.ts +84 -0
  590. package/src/styles.ts +343 -0
  591. package/src/tabwright-agent-skill.test.ts +61 -0
  592. package/src/tabwright-agent-skill.ts +105 -0
  593. package/src/test-declarations.ts +13 -0
  594. package/src/test-utils.test.ts +70 -0
  595. package/src/test-utils.ts +566 -0
  596. package/src/utils.ts +78 -0
  597. package/src/wait-for-page-load.ts +198 -0
  598. package/src/workflow-capability.ts +417 -0
  599. package/src/workflow-script-flow.test.ts +388 -0
@@ -0,0 +1,179 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { chromium } from '@xmorse/playwright-core'
5
+ import { findInstalledChrome } from './browser-install.js'
6
+ // NOTE: browser-config uses chromium synchronously (executablePath) during browser start.
7
+ // Patchright mode only affects connectOverCDP in executor.ts, not browser binary lookup.
8
+
9
+ type BrowserLookupOptions = {
10
+ browserPath?: string
11
+ env?: NodeJS.ProcessEnv
12
+ platform?: NodeJS.Platform
13
+ homeDir?: string
14
+ existsSync?: (filePath: string) => boolean
15
+ }
16
+
17
+ function expandHomeDirectory({ filePath, homeDir }: { filePath: string; homeDir: string }): string {
18
+ if (!filePath.startsWith('~/')) {
19
+ return filePath
20
+ }
21
+ return path.join(homeDir, filePath.slice(2))
22
+ }
23
+
24
+ function dedupePaths(pathsToCheck: string[]): string[] {
25
+ return Array.from(new Set(pathsToCheck))
26
+ }
27
+
28
+ function getPlaywrightChromiumCandidate(): string[] {
29
+ const executablePath = chromium.executablePath()
30
+ if (!executablePath) {
31
+ return []
32
+ }
33
+ return [executablePath]
34
+ }
35
+
36
+ function getPathEntries(env: NodeJS.ProcessEnv): string[] {
37
+ const rawPath = env.PATH || env.Path || ''
38
+ return rawPath
39
+ .split(path.delimiter)
40
+ .filter(Boolean)
41
+ .map((entry) => {
42
+ return entry.trim()
43
+ })
44
+ }
45
+
46
+ function getExecutableNames(platform: NodeJS.Platform): string[] {
47
+ if (platform === 'win32') {
48
+ return ['chrome.exe', 'chromium.exe']
49
+ }
50
+ return ['chrome', 'chromium', 'chromium-browser']
51
+ }
52
+
53
+ function getPathExecutableCandidates({
54
+ platform,
55
+ env,
56
+ }: {
57
+ platform: NodeJS.Platform
58
+ env: NodeJS.ProcessEnv
59
+ }): string[] {
60
+ const executableNames = getExecutableNames(platform)
61
+ return getPathEntries(env).flatMap((entry) => {
62
+ return executableNames.map((name) => {
63
+ return path.join(entry, name)
64
+ })
65
+ })
66
+ }
67
+
68
+ export function getBrowserExecutableCandidates({
69
+ platform = os.platform(),
70
+ env = process.env,
71
+ homeDir = os.homedir(),
72
+ }: Omit<BrowserLookupOptions, 'browserPath' | 'existsSync'> = {}): string[] {
73
+ const platformCandidates = (() => {
74
+ if (platform === 'darwin') {
75
+ return [
76
+ '/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
77
+ '~/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
78
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
79
+ '~/Applications/Chromium.app/Contents/MacOS/Chromium',
80
+ ]
81
+ }
82
+
83
+ if (platform === 'win32') {
84
+ const localAppData = env.LOCALAPPDATA || ''
85
+ const programFiles = env.PROGRAMFILES || 'C:\\Program Files'
86
+ const programFilesX86 = env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
87
+
88
+ return [
89
+ path.join(programFiles, 'Google', 'Chrome for Testing', 'Application', 'chrome.exe'),
90
+ path.join(programFilesX86, 'Google', 'Chrome for Testing', 'Application', 'chrome.exe'),
91
+ path.join(localAppData, 'Google', 'Chrome for Testing', 'Application', 'chrome.exe'),
92
+ path.join(programFiles, 'Chromium', 'Application', 'chromium.exe'),
93
+ path.join(programFilesX86, 'Chromium', 'Application', 'chromium.exe'),
94
+ path.join(localAppData, 'Chromium', 'Application', 'chromium.exe'),
95
+ ]
96
+ }
97
+
98
+ return [
99
+ '/opt/google/chrome-for-testing/chrome',
100
+ '/usr/local/bin/chrome',
101
+ '/usr/bin/chrome',
102
+ '/usr/bin/chromium',
103
+ '/usr/bin/chromium-browser',
104
+ '/snap/bin/chromium',
105
+ ]
106
+ })()
107
+
108
+ const pathCandidates = getPathExecutableCandidates({ platform, env })
109
+ return dedupePaths(
110
+ [...platformCandidates, ...pathCandidates, ...getPlaywrightChromiumCandidate()].map((filePath) => {
111
+ return expandHomeDirectory({ filePath, homeDir })
112
+ }),
113
+ )
114
+ }
115
+
116
+ export function resolveBrowserExecutablePath({
117
+ browserPath,
118
+ env = process.env,
119
+ platform = os.platform(),
120
+ homeDir = os.homedir(),
121
+ existsSync = fs.existsSync,
122
+ }: BrowserLookupOptions = {}): string {
123
+ const explicitPath = browserPath?.trim()
124
+ if (explicitPath) {
125
+ const resolvedExplicitPath = expandHomeDirectory({ filePath: explicitPath, homeDir })
126
+ if (!existsSync(resolvedExplicitPath)) {
127
+ throw new Error(`Browser binary not found at: ${resolvedExplicitPath}`)
128
+ }
129
+ return resolvedExplicitPath
130
+ }
131
+
132
+ const envPath = env.TABWRIGHT_BROWSER_PATH?.trim() || env.PLAYWRITER_BROWSER_PATH?.trim()
133
+ if (envPath) {
134
+ const resolvedEnvPath = expandHomeDirectory({ filePath: envPath, homeDir })
135
+ if (!existsSync(resolvedEnvPath)) {
136
+ throw new Error(`TABWRIGHT_BROWSER_PATH does not exist: ${resolvedEnvPath}`)
137
+ }
138
+ return resolvedEnvPath
139
+ }
140
+
141
+ // Check Chrome downloaded by `tabwright browser install` first (highest priority)
142
+ const installedChrome = findInstalledChrome()
143
+ if (installedChrome) {
144
+ return installedChrome
145
+ }
146
+
147
+ const candidates = getBrowserExecutableCandidates({ platform, env, homeDir })
148
+ const resolvedPath = candidates.find((candidate) => {
149
+ return existsSync(candidate)
150
+ })
151
+
152
+ if (resolvedPath) {
153
+ return resolvedPath
154
+ }
155
+
156
+ const searchedPathsText = candidates.map((candidate) => {
157
+ return `- ${candidate}`
158
+ })
159
+
160
+ throw new Error(
161
+ 'Could not find a supported browser binary. Run `tabwright browser install` to download Chrome for Testing, or pass a binary path.' +
162
+ `\n\nSearched paths:\n- ~/.tabwright/browsers/ (tabwright browser install)\n${searchedPathsText.join('\n')}`,
163
+ )
164
+ }
165
+
166
+ export function shouldUseHeadlessByDefault({
167
+ platform = os.platform(),
168
+ env = process.env,
169
+ }: Omit<BrowserLookupOptions, 'browserPath' | 'homeDir' | 'existsSync'> = {}): boolean {
170
+ if (platform !== 'linux') {
171
+ return false
172
+ }
173
+
174
+ return !env.DISPLAY && !env.WAYLAND_DISPLAY
175
+ }
176
+
177
+ export function getBrowserExecutablePath(browserPath?: string): string {
178
+ return resolveBrowserExecutablePath({ browserPath })
179
+ }
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Download and install Chrome for Testing into ~/.tabwright/browsers/.
3
+ * Similar to agent-browser's install command: fetches the latest stable
4
+ * Chrome for Testing build from Google's official automation channel.
5
+ */
6
+
7
+ import fs from 'node:fs'
8
+ import os from 'node:os'
9
+ import path from 'node:path'
10
+ import { pipeline } from 'node:stream/promises'
11
+ import { Readable } from 'node:stream'
12
+ import { execFileSync } from 'node:child_process'
13
+ import { getTabwrightUserDataDir } from './product-paths.js'
14
+
15
+ const LAST_KNOWN_GOOD_URL =
16
+ 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json'
17
+
18
+ export function getBrowsersDir(): string {
19
+ return path.join(getTabwrightUserDataDir(), 'browsers')
20
+ }
21
+
22
+ function getPlatformKey(): string {
23
+ const platform = os.platform()
24
+ const arch = os.arch()
25
+ if (platform === 'darwin' && arch === 'arm64') {
26
+ return 'mac-arm64'
27
+ }
28
+ if (platform === 'darwin' && arch === 'x64') {
29
+ return 'mac-x64'
30
+ }
31
+ if (platform === 'linux' && arch === 'x64') {
32
+ return 'linux64'
33
+ }
34
+ if (platform === 'win32' && arch === 'x64') {
35
+ return 'win64'
36
+ }
37
+ throw new Error(
38
+ `Unsupported platform for Chrome for Testing download: ${platform}/${arch}. ` +
39
+ `Install Chromium manually and use TABWRIGHT_BROWSER_PATH to point to it.`,
40
+ )
41
+ }
42
+
43
+ /**
44
+ * Find the Chrome binary inside a downloaded Chrome for Testing directory.
45
+ */
46
+ function findChromeBinaryInDir(dir: string): string | null {
47
+ const platform = os.platform()
48
+
49
+ if (platform === 'darwin') {
50
+ const candidates = [
51
+ path.join(dir, 'Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing'),
52
+ path.join(dir, `chrome-${getPlatformKey()}/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing`),
53
+ ]
54
+ for (const candidate of candidates) {
55
+ if (fs.existsSync(candidate)) {
56
+ return candidate
57
+ }
58
+ }
59
+ }
60
+
61
+ if (platform === 'linux') {
62
+ const candidates = [
63
+ path.join(dir, 'chrome'),
64
+ path.join(dir, 'chrome-linux64/chrome'),
65
+ ]
66
+ for (const candidate of candidates) {
67
+ if (fs.existsSync(candidate)) {
68
+ return candidate
69
+ }
70
+ }
71
+ }
72
+
73
+ if (platform === 'win32') {
74
+ const candidates = [
75
+ path.join(dir, 'chrome.exe'),
76
+ path.join(dir, 'chrome-win64/chrome.exe'),
77
+ ]
78
+ for (const candidate of candidates) {
79
+ if (fs.existsSync(candidate)) {
80
+ return candidate
81
+ }
82
+ }
83
+ }
84
+
85
+ return null
86
+ }
87
+
88
+ /**
89
+ * Find Chrome installed by `tabwright browser install` in ~/.tabwright/browsers/.
90
+ * Returns the path to the Chrome binary, or null if not found.
91
+ */
92
+ export function findInstalledChrome(): string | null {
93
+ const browsersDir = getBrowsersDir()
94
+ if (!fs.existsSync(browsersDir)) {
95
+ return null
96
+ }
97
+
98
+ const entries = fs.readdirSync(browsersDir, { withFileTypes: true })
99
+ .filter((e) => {
100
+ return e.isDirectory() && e.name.startsWith('chrome-')
101
+ })
102
+ .sort((a, b) => {
103
+ // Sort descending so newest version is first
104
+ return b.name.localeCompare(a.name)
105
+ })
106
+
107
+ for (const entry of entries) {
108
+ const dir = path.join(browsersDir, entry.name)
109
+ const binary = findChromeBinaryInDir(dir)
110
+ if (binary) {
111
+ return binary
112
+ }
113
+ }
114
+
115
+ return null
116
+ }
117
+
118
+ interface VersionInfo {
119
+ version: string
120
+ downloadUrl: string
121
+ }
122
+
123
+ async function fetchDownloadUrl(): Promise<VersionInfo> {
124
+ const response = await fetch(LAST_KNOWN_GOOD_URL, {
125
+ signal: AbortSignal.timeout(30000),
126
+ })
127
+ if (!response.ok) {
128
+ throw new Error(`Failed to fetch Chrome version info: HTTP ${response.status}`)
129
+ }
130
+
131
+ const data = (await response.json()) as {
132
+ channels: {
133
+ Stable: {
134
+ version: string
135
+ downloads: {
136
+ chrome: Array<{ platform: string; url: string }>
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ const channel = data.channels?.Stable
143
+ if (!channel) {
144
+ throw new Error('No Stable channel found in Chrome for Testing version info')
145
+ }
146
+
147
+ const platformKey = getPlatformKey()
148
+ const download = channel.downloads?.chrome?.find((d) => {
149
+ return d.platform === platformKey
150
+ })
151
+ if (!download) {
152
+ throw new Error(`No Chrome for Testing download found for platform: ${platformKey}`)
153
+ }
154
+
155
+ return { version: channel.version, downloadUrl: download.url }
156
+ }
157
+
158
+ async function downloadFile(url: string, destPath: string): Promise<void> {
159
+ // 5-minute timeout per attempt, 3 retries for transient errors
160
+ const maxAttempts = 3
161
+ let lastError: Error | null = null
162
+
163
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
164
+ try {
165
+ await downloadFileAttempt(url, destPath)
166
+ return
167
+ } catch (error) {
168
+ lastError = error instanceof Error ? error : new Error(String(error))
169
+ // Don't retry on 4xx (permanent errors)
170
+ if (lastError.message.includes('HTTP 4')) {
171
+ throw lastError
172
+ }
173
+ if (attempt < maxAttempts) {
174
+ const delay = attempt * 2000
175
+ console.log(` Download attempt ${attempt} failed, retrying in ${delay / 1000}s...`)
176
+ await new Promise((resolve) => {
177
+ return setTimeout(resolve, delay)
178
+ })
179
+ }
180
+ }
181
+ }
182
+ throw lastError!
183
+ }
184
+
185
+ async function downloadFileAttempt(url: string, destPath: string): Promise<void> {
186
+ const response = await fetch(url, {
187
+ signal: AbortSignal.timeout(300_000), // 5 minute timeout
188
+ })
189
+ if (!response.ok) {
190
+ throw new Error(`Download failed: HTTP ${response.status} for ${url}`)
191
+ }
192
+ if (!response.body) {
193
+ throw new Error('Download failed: no response body')
194
+ }
195
+
196
+ const totalBytes = Number(response.headers.get('content-length') || 0)
197
+ let downloadedBytes = 0
198
+ let lastPct = 0
199
+
200
+ const progressStream = new TransformStream({
201
+ transform(chunk, controller) {
202
+ downloadedBytes += chunk.byteLength
203
+ if (totalBytes > 0) {
204
+ const pct = Math.floor((downloadedBytes / totalBytes) * 100)
205
+ if (pct >= lastPct + 5) {
206
+ lastPct = pct
207
+ const mb = (downloadedBytes / 1_048_576).toFixed(0)
208
+ const totalMb = (totalBytes / 1_048_576).toFixed(0)
209
+ process.stderr.write(`\r ${mb}/${totalMb} MB (${pct}%)`)
210
+ }
211
+ }
212
+ controller.enqueue(chunk)
213
+ },
214
+ })
215
+
216
+ const readableStream = response.body.pipeThrough(progressStream)
217
+ const nodeReadable = Readable.fromWeb(readableStream as any)
218
+ const writeStream = fs.createWriteStream(destPath)
219
+ await pipeline(nodeReadable, writeStream)
220
+ process.stderr.write('\n')
221
+ }
222
+
223
+ function extractZip(zipPath: string, destDir: string): void {
224
+ fs.mkdirSync(destDir, { recursive: true })
225
+ const platform = os.platform()
226
+
227
+ if (platform === 'win32') {
228
+ execFileSync('powershell', [
229
+ '-Command',
230
+ `Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force`,
231
+ ], { stdio: 'pipe' })
232
+ } else {
233
+ execFileSync('unzip', ['-o', '-q', zipPath, '-d', destDir], { stdio: 'pipe' })
234
+ }
235
+ }
236
+
237
+ export async function installChrome(): Promise<{ version: string; binaryPath: string }> {
238
+ console.log('Fetching latest Chrome for Testing version...')
239
+ const { version, downloadUrl } = await fetchDownloadUrl()
240
+
241
+ const browsersDir = getBrowsersDir()
242
+ const destDir = path.join(browsersDir, `chrome-${version}`)
243
+
244
+ // Check if already installed
245
+ const existingBinary = findChromeBinaryInDir(destDir)
246
+ if (existingBinary) {
247
+ console.log(`Chrome ${version} is already installed.`)
248
+ console.log(` Location: ${existingBinary}`)
249
+ return { version, binaryPath: existingBinary }
250
+ }
251
+
252
+ console.log(`Downloading Chrome ${version} for ${getPlatformKey()}...`)
253
+ console.log(` ${downloadUrl}`)
254
+
255
+ const tmpZip = path.join(os.tmpdir(), `tabwright-chrome-${version}.zip`)
256
+ try {
257
+ await downloadFile(downloadUrl, tmpZip)
258
+ console.log('Extracting...')
259
+ extractZip(tmpZip, destDir)
260
+ } finally {
261
+ // Clean up temp zip
262
+ try {
263
+ fs.unlinkSync(tmpZip)
264
+ } catch {
265
+ // ignore cleanup errors
266
+ }
267
+ }
268
+
269
+ const binaryPath = findChromeBinaryInDir(destDir)
270
+ if (!binaryPath) {
271
+ // Clean up failed extraction
272
+ fs.rmSync(destDir, { recursive: true, force: true })
273
+ throw new Error('Chrome was downloaded but the binary could not be found in the extracted archive.')
274
+ }
275
+
276
+ // Ensure binary is executable on Unix
277
+ if (os.platform() !== 'win32') {
278
+ fs.chmodSync(binaryPath, 0o755)
279
+ }
280
+
281
+ console.log(`Chrome ${version} installed successfully.`)
282
+ console.log(` Location: ${binaryPath}`)
283
+ return { version, binaryPath }
284
+ }
@@ -0,0 +1,75 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { spawn } from 'node:child_process'
4
+ import { EXTENSION_IDS } from './utils.js'
5
+ import { getTabwrightUserDataDir } from './product-paths.js'
6
+
7
+ export type BrowserLaunchOptions = {
8
+ extensionPath: string
9
+ userDataDir: string
10
+ headless: boolean
11
+ noSandbox?: boolean
12
+ url?: string
13
+ }
14
+
15
+ export function getDefaultBrowserUserDataDir(): string {
16
+ return path.join(getTabwrightUserDataDir(), 'browser-profile')
17
+ }
18
+
19
+ export function getBrowserLaunchArgs({
20
+ extensionPath,
21
+ userDataDir,
22
+ headless,
23
+ noSandbox = false,
24
+ url = 'about:blank',
25
+ }: BrowserLaunchOptions): string[] {
26
+ const recordingFlags = EXTENSION_IDS.map((extensionId) => {
27
+ return `--allowlisted-extension-id=${extensionId}`
28
+ })
29
+
30
+ const args = [
31
+ `--user-data-dir=${path.resolve(userDataDir)}`,
32
+ '--profile-directory=Default',
33
+ '--no-first-run',
34
+ '--no-default-browser-check',
35
+ '--auto-accept-this-tab-capture',
36
+ ...recordingFlags,
37
+ `--disable-extensions-except=${path.resolve(extensionPath)}`,
38
+ `--load-extension=${path.resolve(extensionPath)}`,
39
+ ]
40
+
41
+ if (headless) {
42
+ args.push('--headless=new')
43
+ }
44
+
45
+ if (noSandbox) {
46
+ args.push('--no-sandbox', '--disable-setuid-sandbox')
47
+ }
48
+
49
+ args.push(url)
50
+ return args
51
+ }
52
+
53
+ export function startBrowserProcess({
54
+ browserPath,
55
+ args,
56
+ userDataDir,
57
+ }: {
58
+ browserPath: string
59
+ args: string[]
60
+ userDataDir: string
61
+ }): { pid: number } {
62
+ fs.mkdirSync(path.resolve(userDataDir), { recursive: true })
63
+
64
+ const browserProcess = spawn(browserPath, args, {
65
+ detached: true,
66
+ stdio: 'ignore',
67
+ })
68
+ browserProcess.unref()
69
+
70
+ if (!browserProcess.pid) {
71
+ throw new Error(`Failed to start browser process for ${browserPath}`)
72
+ }
73
+
74
+ return { pid: browserProcess.pid }
75
+ }