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,572 @@
1
+ /**
2
+ * Shared utilities for connecting to the relay server.
3
+ * Used by both MCP and CLI.
4
+ */
5
+
6
+ import { spawn } from 'node:child_process'
7
+ import path from 'node:path'
8
+ import { fileURLToPath } from 'node:url'
9
+ import pc from 'picocolors'
10
+ import { getListeningPidsForPort, killPortProcess } from './kill-port.js'
11
+ import { RELAY_FEATURES } from './protocol.js'
12
+ import { VERSION, sleep, LOG_FILE_PATH } from './utils.js'
13
+
14
+ const __filename = fileURLToPath(import.meta.url)
15
+ const __dirname = path.dirname(__filename)
16
+
17
+ export const RELAY_PORT = Number(process.env.TABWRIGHT_PORT || process.env.PLAYWRITER_PORT) || 19988
18
+ export const LOCAL_RELAY_HOSTS = ['127.0.0.1', 'localhost', '[::1]'] as const
19
+
20
+ export type ExtensionStatus = {
21
+ extensionId: string
22
+ stableKey?: string
23
+ browser: string | null
24
+ profile: { email: string; id: string } | null
25
+ activeTargets: number
26
+ playwriterVersion: string | null
27
+ protocolVersion?: number
28
+ features?: string[]
29
+ connectionHealth?: 'ready' | 'limited' | 'legacy'
30
+ missingFeatures?: string[]
31
+ }
32
+
33
+ /**
34
+ * Select an extension only when the choice is unambiguous.
35
+ * A single connection remains the backward-compatible default. With multiple
36
+ * connections, an enabled tab is a useful signal only when exactly one
37
+ * extension has one.
38
+ */
39
+ export function selectImplicitExtension(extensions: ExtensionStatus[]): ExtensionStatus | null {
40
+ if (extensions.length === 1) {
41
+ return extensions[0]!
42
+ }
43
+
44
+ const activeExtensions = extensions.filter((extension) => {
45
+ return extension.activeTargets > 0
46
+ })
47
+ if (activeExtensions.length === 1) {
48
+ return activeExtensions[0]!
49
+ }
50
+
51
+ return null
52
+ }
53
+
54
+ export function getLocalRelayHttpBaseUrls(port: number = RELAY_PORT): string[] {
55
+ return LOCAL_RELAY_HOSTS.map((host) => {
56
+ return `http://${host}:${port}`
57
+ })
58
+ }
59
+
60
+ async function fetchLocalRelayPath({
61
+ path,
62
+ port = RELAY_PORT,
63
+ timeoutMs = 2000,
64
+ init,
65
+ accept = () => {
66
+ return true
67
+ },
68
+ }: {
69
+ path: string
70
+ port?: number
71
+ timeoutMs?: number
72
+ init?: RequestInit
73
+ accept?: (response: Response) => boolean
74
+ }): Promise<{ response: Response; httpBaseUrl: string } | null> {
75
+ const attempts = getLocalRelayHttpBaseUrls(port)
76
+
77
+ for (const httpBaseUrl of attempts) {
78
+ try {
79
+ const response = await fetch(`${httpBaseUrl}${path}`, {
80
+ ...init,
81
+ signal: AbortSignal.timeout(timeoutMs),
82
+ })
83
+ if (accept(response)) {
84
+ return { response, httpBaseUrl }
85
+ }
86
+ } catch {}
87
+ }
88
+
89
+ return null
90
+ }
91
+
92
+ export async function getLocalRelayHttpBaseUrl(port: number = RELAY_PORT): Promise<string> {
93
+ const result = await fetchLocalRelayPath({
94
+ path: '/version',
95
+ port,
96
+ timeoutMs: 500,
97
+ accept: (response) => {
98
+ return response.ok
99
+ },
100
+ })
101
+ if (result) {
102
+ return result.httpBaseUrl
103
+ }
104
+ return getLocalRelayHttpBaseUrls(port)[0]!
105
+ }
106
+
107
+ export async function getRelayServerVersion(port: number = RELAY_PORT): Promise<string | null> {
108
+ const result = await fetchLocalRelayPath({
109
+ path: '/version',
110
+ port,
111
+ timeoutMs: 2000,
112
+ accept: (response) => {
113
+ return response.ok
114
+ },
115
+ })
116
+ if (!result) {
117
+ return null
118
+ }
119
+ const data = (await result.response.json()) as { version: string }
120
+ return data.version
121
+ }
122
+
123
+ export async function getRelayServerFeatures(port: number = RELAY_PORT): Promise<string[] | null> {
124
+ const result = await fetchLocalRelayPath({
125
+ path: '/features',
126
+ port,
127
+ timeoutMs: 2000,
128
+ accept: (response) => {
129
+ return response.ok
130
+ },
131
+ })
132
+ if (!result) {
133
+ return null
134
+ }
135
+ const data: unknown = await result.response.json()
136
+ if (!data || typeof data !== 'object') {
137
+ return null
138
+ }
139
+ const features = (data as { features?: unknown }).features
140
+ if (!Array.isArray(features) || !features.every((feature) => typeof feature === 'string')) {
141
+ return null
142
+ }
143
+ return features
144
+ }
145
+
146
+ export function supportsRelayFeatures(options: {
147
+ features: readonly string[] | null
148
+ requiredFeatures?: readonly string[]
149
+ }): boolean {
150
+ const requiredFeatures = options.requiredFeatures || RELAY_FEATURES
151
+ const features = options.features
152
+ if (!features) {
153
+ return false
154
+ }
155
+ return requiredFeatures.every((feature) => {
156
+ return features.includes(feature)
157
+ })
158
+ }
159
+
160
+ function acceptsRelayVersion({
161
+ version,
162
+ expectedVersion,
163
+ minimumVersion,
164
+ }: {
165
+ version: string | null
166
+ expectedVersion?: string
167
+ minimumVersion?: string
168
+ }): boolean {
169
+ if (!version) {
170
+ return false
171
+ }
172
+ if (expectedVersion) {
173
+ return version === expectedVersion
174
+ }
175
+ if (minimumVersion) {
176
+ return compareVersions(version, minimumVersion) >= 0
177
+ }
178
+ return true
179
+ }
180
+
181
+ /**
182
+ * Poll /version until a relay responds or timeout expires.
183
+ * Used during startup races where a relay may have bound the port
184
+ * but isn't serving HTTP yet (issue #75).
185
+ */
186
+ export async function waitForRelayVersion({
187
+ port = RELAY_PORT,
188
+ timeoutMs = 2000,
189
+ intervalMs = 200,
190
+ expectedVersion,
191
+ minimumVersion,
192
+ }: {
193
+ port?: number
194
+ timeoutMs?: number
195
+ intervalMs?: number
196
+ /** Keep polling until this exact relay version responds. Takes precedence over minimumVersion. */
197
+ expectedVersion?: string
198
+ /** Keep polling until this relay version or a newer one responds. */
199
+ minimumVersion?: string
200
+ } = {}): Promise<string | null> {
201
+ const end = Date.now() + timeoutMs
202
+ while (Date.now() < end) {
203
+ const version = await getRelayServerVersion(port)
204
+ if (acceptsRelayVersion({ version, expectedVersion, minimumVersion })) {
205
+ return version
206
+ }
207
+ await sleep(intervalMs)
208
+ }
209
+ return null
210
+ }
211
+
212
+ async function waitForCurrentRelayReadiness(options: {
213
+ port: number
214
+ timeoutMs: number
215
+ intervalMs?: number
216
+ }): Promise<string | null> {
217
+ const end = Date.now() + options.timeoutMs
218
+ const intervalMs = options.intervalMs || 200
219
+ while (Date.now() < end) {
220
+ const version = await getRelayServerVersion(options.port)
221
+ if (version && compareVersions(version, VERSION) > 0) {
222
+ return version
223
+ }
224
+ if (version === VERSION) {
225
+ const features = await getRelayServerFeatures(options.port)
226
+ if (supportsRelayFeatures({ features })) {
227
+ return version
228
+ }
229
+ }
230
+ await sleep(intervalMs)
231
+ }
232
+ return null
233
+ }
234
+
235
+ export async function getExtensionStatus(port: number = RELAY_PORT): Promise<{
236
+ connected: boolean
237
+ activeTargets: number
238
+ playwriterVersion: string | null
239
+ protocolVersion?: number
240
+ features?: string[]
241
+ connectionHealth?: 'ready' | 'limited' | 'legacy'
242
+ missingFeatures?: string[]
243
+ } | null> {
244
+ const result = await fetchLocalRelayPath({
245
+ path: '/extension/status',
246
+ port,
247
+ timeoutMs: 500,
248
+ accept: (response) => {
249
+ return response.ok
250
+ },
251
+ })
252
+ if (!result) {
253
+ return null
254
+ }
255
+ return (await result.response.json()) as {
256
+ connected: boolean
257
+ activeTargets: number
258
+ playwriterVersion: string | null
259
+ protocolVersion?: number
260
+ features?: string[]
261
+ connectionHealth?: 'ready' | 'limited' | 'legacy'
262
+ missingFeatures?: string[]
263
+ }
264
+ }
265
+
266
+ export async function getExtensionsStatus(port: number = RELAY_PORT): Promise<ExtensionStatus[]> {
267
+ const result = await fetchLocalRelayPath({
268
+ path: '/extensions/status',
269
+ port,
270
+ timeoutMs: 2000,
271
+ accept: (response) => {
272
+ return response.ok
273
+ },
274
+ })
275
+ if (result) {
276
+ const data = (await result.response.json()) as {
277
+ extensions: ExtensionStatus[]
278
+ }
279
+ return data.extensions || []
280
+ }
281
+
282
+ const fallbackResult = await fetchLocalRelayPath({
283
+ path: '/extension/status',
284
+ port,
285
+ timeoutMs: 2000,
286
+ accept: (response) => {
287
+ return response.ok
288
+ },
289
+ })
290
+ if (!fallbackResult) {
291
+ return []
292
+ }
293
+
294
+ const fallbackData = (await fallbackResult.response.json()) as {
295
+ connected: boolean
296
+ activeTargets: number
297
+ browser: string | null
298
+ profile: { email: string; id: string } | null
299
+ playwriterVersion?: string | null
300
+ protocolVersion?: number
301
+ features?: string[]
302
+ connectionHealth?: 'ready' | 'limited' | 'legacy'
303
+ missingFeatures?: string[]
304
+ }
305
+
306
+ if (!fallbackData?.connected) {
307
+ return []
308
+ }
309
+
310
+ return [
311
+ {
312
+ extensionId: 'default',
313
+ stableKey: undefined,
314
+ browser: fallbackData.browser,
315
+ profile: fallbackData.profile,
316
+ activeTargets: fallbackData.activeTargets,
317
+ playwriterVersion: fallbackData.playwriterVersion || null,
318
+ protocolVersion: fallbackData.protocolVersion,
319
+ features: fallbackData.features,
320
+ connectionHealth: fallbackData.connectionHealth,
321
+ missingFeatures: fallbackData.missingFeatures,
322
+ },
323
+ ]
324
+ }
325
+
326
+ function getExtensionSettleSignature(extensions: ExtensionStatus[]): string {
327
+ return extensions
328
+ .map((extension) => {
329
+ return JSON.stringify({
330
+ extensionId: extension.extensionId,
331
+ stableKey: extension.stableKey ?? null,
332
+ activeTargets: extension.activeTargets,
333
+ })
334
+ })
335
+ .sort()
336
+ .join('\n')
337
+ }
338
+
339
+ /**
340
+ * Wait for at least one extension to appear in extensions status.
341
+ * Returns connected extension entries, or [] on timeout.
342
+ */
343
+ export async function waitForConnectedExtensions(
344
+ options: {
345
+ port?: number
346
+ timeoutMs?: number
347
+ pollIntervalMs?: number
348
+ /** Wait for the connection snapshot to remain unchanged before returning. Default: 0. */
349
+ settleMs?: number
350
+ logger?: { log: (...args: any[]) => void }
351
+ } = {},
352
+ ): Promise<ExtensionStatus[]> {
353
+ const { port = RELAY_PORT, timeoutMs = 5000, pollIntervalMs = 200, settleMs = 0, logger } = options
354
+ const startTime = Date.now()
355
+ let latestNonEmpty: ExtensionStatus[] = []
356
+ let latestSnapshotWasConnected = false
357
+ let stableSignature: string | null = null
358
+ let stableSince = 0
359
+
360
+ logger?.log(pc.dim('Waiting for extension to connect...'))
361
+
362
+ while (Date.now() - startTime < timeoutMs) {
363
+ const extensions = await getExtensionsStatus(port)
364
+ if (extensions.length > 0) {
365
+ latestSnapshotWasConnected = true
366
+ latestNonEmpty = extensions
367
+ const signature = getExtensionSettleSignature(extensions)
368
+ if (signature !== stableSignature) {
369
+ stableSignature = signature
370
+ stableSince = Date.now()
371
+ }
372
+ if (settleMs === 0 || Date.now() - stableSince >= settleMs) {
373
+ logger?.log(pc.green('Extension connected'))
374
+ return latestNonEmpty
375
+ }
376
+ } else if (latestNonEmpty.length > 0) {
377
+ latestSnapshotWasConnected = false
378
+ stableSignature = null
379
+ }
380
+ await sleep(pollIntervalMs)
381
+ }
382
+
383
+ if (latestSnapshotWasConnected && latestNonEmpty.length > 0) {
384
+ logger?.log(pc.green('Extension connected'))
385
+ return latestNonEmpty
386
+ }
387
+
388
+ logger?.log(pc.yellow('Extension did not connect within timeout'))
389
+ return []
390
+ }
391
+
392
+ async function killRelayServer(options: { port: number; waitForFreeMs?: number }): Promise<void> {
393
+ const { port, waitForFreeMs = 3000 } = options
394
+
395
+ try {
396
+ await killPortProcess({ port })
397
+ } catch {
398
+ return
399
+ }
400
+
401
+ const startTime = Date.now()
402
+ while (Date.now() - startTime < waitForFreeMs) {
403
+ const pids = await getListeningPidsForPort({ port }).catch(() => [])
404
+ if (pids.length === 0) {
405
+ return
406
+ }
407
+ await sleep(100)
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Compare two semver versions. Returns:
413
+ * - negative if v1 < v2
414
+ * - 0 if v1 === v2
415
+ * - positive if v1 > v2
416
+ */
417
+ export function compareVersions(v1: string, v2: string): number {
418
+ const parts1 = v1.split('.').map(Number)
419
+ const parts2 = v2.split('.').map(Number)
420
+ const len = Math.max(parts1.length, parts2.length)
421
+
422
+ for (let i = 0; i < len; i++) {
423
+ const p1 = parts1[i] || 0
424
+ const p2 = parts2[i] || 0
425
+ if (p1 !== p2) {
426
+ return p1 - p2
427
+ }
428
+ }
429
+ return 0
430
+ }
431
+
432
+ /**
433
+ * Check if the running Tabwright package is older than the version the extension was built with.
434
+ * The extension bundles the Tabwright version at build time. If the extension reports a newer
435
+ * version, it means the user's CLI/MCP needs updating.
436
+ * Returns a warning message if outdated, null otherwise.
437
+ */
438
+ export function getExtensionOutdatedWarning(extensionTabwrightVersion: string | null | undefined): string | null {
439
+ if (!extensionTabwrightVersion) {
440
+ return null
441
+ }
442
+ if (compareVersions(extensionTabwrightVersion, VERSION) > 0) {
443
+ return `Tabwright ${VERSION} is outdated (extension requires ${extensionTabwrightVersion}). Run \`npm install -g tabwright@latest\` or update the tabwright package in your project.`
444
+ }
445
+ return null
446
+ }
447
+
448
+ export interface EnsureRelayServerOptions {
449
+ logger?: { log: (...args: any[]) => void }
450
+ /** If true, will kill and restart server on version mismatch. Default: true */
451
+ restartOnVersionMismatch?: boolean
452
+ /** Pass additional environment variables to the relay server process */
453
+ env?: Record<string, string>
454
+ }
455
+
456
+ // Module-level dedup: if ensureRelayServer is called concurrently within the
457
+ // same process (e.g. two MCP tool handlers at once), only one spawn runs.
458
+ let pendingEnsure: Promise<true | undefined> | null = null
459
+
460
+ /**
461
+ * Ensures the relay server is running. Starts it if not running.
462
+ * Optionally restarts on version mismatch.
463
+ * Concurrent calls within the same process are deduplicated.
464
+ */
465
+ export async function ensureRelayServer(options: EnsureRelayServerOptions = {}): Promise<true | undefined> {
466
+ if (pendingEnsure) {
467
+ return pendingEnsure
468
+ }
469
+ pendingEnsure = ensureRelayServerImpl(options).finally(() => {
470
+ pendingEnsure = null
471
+ })
472
+ return pendingEnsure
473
+ }
474
+
475
+ async function ensureRelayServerImpl(options: EnsureRelayServerOptions = {}): Promise<true | undefined> {
476
+ const { logger, restartOnVersionMismatch = true, env: additionalEnv } = options
477
+ const serverVersion = await getRelayServerVersion(RELAY_PORT)
478
+ const sameVersionFeatures = serverVersion === VERSION ? await getRelayServerFeatures(RELAY_PORT) : null
479
+
480
+ if (serverVersion === VERSION && supportsRelayFeatures({ features: sameVersionFeatures })) {
481
+ return
482
+ }
483
+
484
+ // Don't restart if server version is higher than our version.
485
+ // This prevents older clients from killing a newer server.
486
+ if (serverVersion !== null && compareVersions(serverVersion, VERSION) > 0) {
487
+ return
488
+ }
489
+
490
+ if (serverVersion !== null) {
491
+ if (restartOnVersionMismatch) {
492
+ const reason =
493
+ serverVersion === VERSION
494
+ ? `CDP relay server is missing current features (${RELAY_FEATURES.join(', ')}), restarting...`
495
+ : `CDP relay server version mismatch (server: ${serverVersion}, client: ${VERSION}), restarting...`
496
+ logger?.log(pc.yellow(reason))
497
+ await killRelayServer({ port: RELAY_PORT })
498
+ } else {
499
+ // Server is running but different version, just use it
500
+ return
501
+ }
502
+ } else {
503
+ const listeningPids = await getListeningPidsForPort({ port: RELAY_PORT }).catch(() => [])
504
+ if (listeningPids.length > 0) {
505
+ // Something is on the port but /version didn't respond. It might be a
506
+ // relay that's still starting (race with another CLI/MCP instance).
507
+ // Poll /version briefly before deciding to kill it (issue #75).
508
+ const foundVersion = await waitForRelayVersion({ port: RELAY_PORT })
509
+ if (foundVersion) {
510
+ // A relay came up while we waited; only reuse the current version when
511
+ // it also exposes the feature contract expected by this build.
512
+ if (compareVersions(foundVersion, VERSION) > 0) {
513
+ return
514
+ }
515
+ if (foundVersion === VERSION) {
516
+ const features = await getRelayServerFeatures(RELAY_PORT)
517
+ if (supportsRelayFeatures({ features })) {
518
+ return
519
+ }
520
+ }
521
+ if (!restartOnVersionMismatch) {
522
+ return
523
+ }
524
+ const reason =
525
+ foundVersion === VERSION
526
+ ? `CDP relay server is missing current features (${RELAY_FEATURES.join(', ')}), restarting...`
527
+ : `CDP relay server version mismatch (server: ${foundVersion}, client: ${VERSION}), restarting...`
528
+ logger?.log(pc.yellow(reason))
529
+ } else {
530
+ logger?.log(
531
+ pc.yellow(
532
+ `Port ${RELAY_PORT} is already in use (pid(s): ${listeningPids.join(', ')}). Attempting to stop the existing process...`,
533
+ ),
534
+ )
535
+ }
536
+ await killRelayServer({ port: RELAY_PORT })
537
+ }
538
+
539
+ logger?.log(pc.dim('CDP relay server not running, starting it...'))
540
+ }
541
+
542
+ // Detect if we're running from source (.ts) or compiled (.js)
543
+ // This handles: tsx, vite-node, ts-node, or direct node on compiled output
544
+ const isRunningFromSource = __filename.endsWith('.ts')
545
+ const scriptPath = isRunningFromSource
546
+ ? path.resolve(__dirname, './start-relay-server.ts')
547
+ : path.resolve(__dirname, './start-relay-server.js')
548
+
549
+ const serverProcess = spawn(isRunningFromSource ? 'tsx' : process.execPath, [scriptPath], {
550
+ detached: true,
551
+ stdio: 'ignore',
552
+ env: { ...process.env, ...additionalEnv },
553
+ })
554
+
555
+ serverProcess.unref()
556
+
557
+ const startTimeoutMs = 5000
558
+ const startTime = Date.now()
559
+
560
+ const newVersion = await waitForCurrentRelayReadiness({
561
+ port: RELAY_PORT,
562
+ timeoutMs: startTimeoutMs,
563
+ })
564
+ if (newVersion && compareVersions(newVersion, VERSION) >= 0) {
565
+ logger?.log(pc.green('CDP relay server started successfully'))
566
+ await sleep(1000)
567
+ return true
568
+ }
569
+
570
+ const waitedMs = Date.now() - startTime
571
+ throw new Error(`Failed to start CDP relay server within ${waitedMs}ms. Check logs at: ${LOG_FILE_PATH}`)
572
+ }