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,872 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { getTabwrightUserDataDir } from './product-paths.js'
4
+ import { getSavedRrwebRecordingWithEvents } from './rrweb-recording-relay.js'
5
+ import type { RrwebEvent } from './protocol.js'
6
+
7
+ // rrweb.record already stores rrweb-snapshot serialized DOM nodes in full snapshots and mutation adds.
8
+ // This index keeps raw replay as evidence and derives AI-readable labels, fields, and selector hints; rrweb ids are recording-scoped, not future selectors.
9
+ type ReplayNodeType = 0 | 1 | 2 | 3 | 4 | 5
10
+ type ReplayActionKind = 'click' | 'input'
11
+
12
+ interface ReplayDomNode {
13
+ id: number
14
+ type: ReplayNodeType
15
+ tagName?: string
16
+ textContent?: string
17
+ attributes: Record<string, string>
18
+ childIds: number[]
19
+ parentId?: number
20
+ }
21
+
22
+ interface ReplayDomState {
23
+ nodes: Map<number, ReplayDomNode>
24
+ }
25
+
26
+ export interface ReplayAiNodeSummary {
27
+ id: number
28
+ type: string
29
+ tagName?: string
30
+ text?: string
31
+ label?: string
32
+ role?: string
33
+ name?: string
34
+ placeholder?: string
35
+ className?: string
36
+ selectorHints: string[]
37
+ ancestorText: string[]
38
+ }
39
+
40
+ export interface ReplayAiAction {
41
+ kind: ReplayActionKind
42
+ timestamp: number
43
+ relativeTime?: number
44
+ nodeId?: number
45
+ label: string
46
+ value?: string
47
+ checked?: boolean
48
+ node?: ReplayAiNodeSummary
49
+ }
50
+
51
+ export interface ReplayAiField {
52
+ key: string
53
+ label: string
54
+ nodeId?: number
55
+ selectorHints: string[]
56
+ value?: string
57
+ checked?: boolean
58
+ actionCount: number
59
+ updatedAt: number
60
+ }
61
+
62
+ export interface ReplayAiIndexStats {
63
+ eventCount: number
64
+ fullSnapshotCount: number
65
+ mutationEventCount: number
66
+ clickEventCount: number
67
+ inputEventCount: number
68
+ annotationCount: number
69
+ }
70
+
71
+ export interface ReplayAiAnnotationRect {
72
+ x: number
73
+ y: number
74
+ width: number
75
+ height: number
76
+ }
77
+
78
+ export interface ReplayAiAnnotationTarget {
79
+ tagName?: string
80
+ label?: string
81
+ text?: string
82
+ role?: string
83
+ name?: string
84
+ placeholder?: string
85
+ selectorHints: string[]
86
+ rect?: ReplayAiAnnotationRect
87
+ }
88
+
89
+ export interface ReplayAiAnnotation {
90
+ id: string
91
+ text: string
92
+ timestamp: number
93
+ relativeTime?: number
94
+ url?: string
95
+ target?: ReplayAiAnnotationTarget
96
+ }
97
+
98
+ export interface ReplayAiIndex {
99
+ schemaVersion: 1
100
+ replayId: string
101
+ url?: string
102
+ generatedAt: number
103
+ stats: ReplayAiIndexStats
104
+ actions: ReplayAiAction[]
105
+ fields: ReplayAiField[]
106
+ annotations: ReplayAiAnnotation[]
107
+ interactiveElements: ReplayAiNodeSummary[]
108
+ pageText: string[]
109
+ warnings: string[]
110
+ }
111
+
112
+ export interface SavedReplayAiIndex {
113
+ id: string
114
+ replayId: string
115
+ path: string
116
+ generatedAt: number
117
+ size: number
118
+ actionCount: number
119
+ fieldCount: number
120
+ url?: string
121
+ }
122
+
123
+ const MAX_AI_ACTIONS = 200
124
+ const MAX_INTERACTIVE_ELEMENTS = 120
125
+ const MAX_PAGE_TEXTS = 160
126
+
127
+ function getReplayAiIndexesDir(): string {
128
+ return path.join(getTabwrightUserDataDir(), 'replay-ai-indexes')
129
+ }
130
+
131
+ function getReplayAiIndexesIndexPath(): string {
132
+ return path.join(getReplayAiIndexesDir(), 'index.json')
133
+ }
134
+
135
+ function getReplayAiIndexPath(replayId: string): string {
136
+ return path.join(getReplayAiIndexesDir(), `${replayId}.json`)
137
+ }
138
+
139
+ function isRecord(value: unknown): value is Record<string, unknown> {
140
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
141
+ }
142
+
143
+ function optionalString(value: unknown): string | undefined {
144
+ return typeof value === 'string' && value.trim() ? value : undefined
145
+ }
146
+
147
+ function numberValue(value: unknown): number | undefined {
148
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined
149
+ }
150
+
151
+ function stringArrayValue(value: unknown): string[] {
152
+ if (!Array.isArray(value)) {
153
+ return []
154
+ }
155
+ return value.filter((item): item is string => {
156
+ return typeof item === 'string' && item.length > 0
157
+ })
158
+ }
159
+
160
+ function normalizeText(value: string | undefined): string {
161
+ return (value || '').replace(/\s+/g, ' ').trim()
162
+ }
163
+
164
+ function uniqueStrings(values: string[]): string[] {
165
+ return Array.from(new Set(values.filter((value) => {
166
+ return value.length > 0
167
+ })))
168
+ }
169
+
170
+ function recordToStringMap(value: unknown): Record<string, string> {
171
+ if (!isRecord(value)) {
172
+ return {}
173
+ }
174
+ return Object.fromEntries(
175
+ Object.entries(value)
176
+ .map(([key, item]) => {
177
+ if (typeof item === 'string') {
178
+ return [key, item] as const
179
+ }
180
+ if (typeof item === 'number' || typeof item === 'boolean') {
181
+ return [key, String(item)] as const
182
+ }
183
+ if (isRecord(item)) {
184
+ return [key, JSON.stringify(item)] as const
185
+ }
186
+ return null
187
+ })
188
+ .filter((entry): entry is readonly [string, string] => {
189
+ return Boolean(entry)
190
+ }),
191
+ )
192
+ }
193
+
194
+ function getEventType(event: RrwebEvent): number | undefined {
195
+ return numberValue(event.type)
196
+ }
197
+
198
+ function getEventTimestamp(event: RrwebEvent): number | undefined {
199
+ return numberValue(event.timestamp)
200
+ }
201
+
202
+ function getEventData(event: RrwebEvent): Record<string, unknown> {
203
+ return isRecord(event.data) ? event.data : {}
204
+ }
205
+
206
+ function getNodeId(value: unknown): number | undefined {
207
+ return numberValue(isRecord(value) ? value.id : undefined)
208
+ }
209
+
210
+ function readReplayNode(rawNode: unknown, parentId?: number): ReplayDomNode | null {
211
+ if (!isRecord(rawNode)) {
212
+ return null
213
+ }
214
+ const id = numberValue(rawNode.id)
215
+ const rawType = numberValue(rawNode.type)
216
+ if (!id || rawType === undefined || ![0, 1, 2, 3, 4, 5].includes(rawType)) {
217
+ return null
218
+ }
219
+ const type = rawType as ReplayNodeType
220
+ const childNodes = Array.isArray(rawNode.childNodes) ? rawNode.childNodes : []
221
+ return {
222
+ id,
223
+ type,
224
+ tagName: optionalString(rawNode.tagName)?.toLowerCase(),
225
+ textContent: optionalString(rawNode.textContent),
226
+ attributes: recordToStringMap(rawNode.attributes),
227
+ childIds: childNodes
228
+ .map((child) => {
229
+ return getNodeId(child)
230
+ })
231
+ .filter((nodeId): nodeId is number => {
232
+ return nodeId !== undefined
233
+ }),
234
+ parentId,
235
+ }
236
+ }
237
+
238
+ function addReplayNode(state: ReplayDomState, rawNode: unknown, parentId?: number): void {
239
+ const node = readReplayNode(rawNode, parentId)
240
+ const childNodes = isRecord(rawNode) && Array.isArray(rawNode.childNodes) ? rawNode.childNodes : []
241
+ if (!node && childNodes.length === 0) {
242
+ return
243
+ }
244
+ if (!node) {
245
+ childNodes.forEach((child) => {
246
+ addReplayNode(state, child, parentId)
247
+ })
248
+ return
249
+ }
250
+ state.nodes.set(node.id, node)
251
+ childNodes.forEach((child) => {
252
+ addReplayNode(state, child, node.id)
253
+ })
254
+ }
255
+
256
+ function removeReplayNode(state: ReplayDomState, nodeId: number): void {
257
+ const node = state.nodes.get(nodeId)
258
+ if (!node) {
259
+ return
260
+ }
261
+ node.childIds.forEach((childId) => {
262
+ removeReplayNode(state, childId)
263
+ })
264
+ state.nodes.delete(nodeId)
265
+ }
266
+
267
+ function applyMutationEvent(state: ReplayDomState, data: Record<string, unknown>): void {
268
+ const removes = Array.isArray(data.removes) ? data.removes : []
269
+ removes
270
+ .map((remove) => {
271
+ return isRecord(remove) ? numberValue(remove.id) : undefined
272
+ })
273
+ .filter((nodeId): nodeId is number => {
274
+ return nodeId !== undefined
275
+ })
276
+ .forEach((nodeId) => {
277
+ removeReplayNode(state, nodeId)
278
+ })
279
+
280
+ const adds = Array.isArray(data.adds) ? data.adds : []
281
+ adds.forEach((add) => {
282
+ if (!isRecord(add)) {
283
+ return
284
+ }
285
+ addReplayNode(state, add.node, numberValue(add.parentId))
286
+ })
287
+
288
+ const texts = Array.isArray(data.texts) ? data.texts : []
289
+ texts.forEach((text) => {
290
+ if (!isRecord(text)) {
291
+ return
292
+ }
293
+ const nodeId = numberValue(text.id)
294
+ const node = nodeId === undefined ? undefined : state.nodes.get(nodeId)
295
+ if (!node) {
296
+ return
297
+ }
298
+ node.textContent = optionalString(text.value) || ''
299
+ })
300
+
301
+ const attributes = Array.isArray(data.attributes) ? data.attributes : []
302
+ attributes.forEach((attribute) => {
303
+ if (!isRecord(attribute)) {
304
+ return
305
+ }
306
+ const nodeId = numberValue(attribute.id)
307
+ const node = nodeId === undefined ? undefined : state.nodes.get(nodeId)
308
+ if (!node) {
309
+ return
310
+ }
311
+ node.attributes = {
312
+ ...node.attributes,
313
+ ...recordToStringMap(attribute.attributes),
314
+ }
315
+ })
316
+ }
317
+
318
+ function isNoisyText(value: string): boolean {
319
+ return /to pick up a draggable item/i.test(value) || value === 'SCRIPT_PLACEHOLDER'
320
+ }
321
+
322
+ function nodeText(state: ReplayDomState, nodeId: number, depth = 0): string {
323
+ const node = state.nodes.get(nodeId)
324
+ if (!node || depth > 20) {
325
+ return ''
326
+ }
327
+ const childText = node.childIds
328
+ .map((childId) => {
329
+ return nodeText(state, childId, depth + 1)
330
+ })
331
+ .filter((text) => {
332
+ return text.length > 0
333
+ })
334
+ .join(' ')
335
+ return normalizeText([node.textContent, childText].filter(Boolean).join(' '))
336
+ }
337
+
338
+ function nodeAndAncestors(state: ReplayDomState, nodeId: number, maxDepth: number): ReplayDomNode[] {
339
+ const current = state.nodes.get(nodeId)
340
+ if (!current) {
341
+ return []
342
+ }
343
+ const ancestors: ReplayDomNode[] = [current]
344
+ let next = current.parentId === undefined ? undefined : state.nodes.get(current.parentId)
345
+ let depth = 1
346
+ while (next && depth < maxDepth) {
347
+ ancestors.push(next)
348
+ next = next.parentId === undefined ? undefined : state.nodes.get(next.parentId)
349
+ depth += 1
350
+ }
351
+ return ancestors
352
+ }
353
+
354
+ function actionLabelFromText(value: string): string | undefined {
355
+ const text = normalizeText(value)
356
+ if (!text || text.length > 120 || isNoisyText(text)) {
357
+ return undefined
358
+ }
359
+ const matchers: Array<{ label: string; pattern: RegExp }> = [
360
+ { label: '重新编辑', pattern: /重新编辑/ },
361
+ { label: '继续编辑', pattern: /继续编辑/ },
362
+ { label: 'Add entry', pattern: /add entry/i },
363
+ { label: '新增', pattern: /新增|添加/ },
364
+ { label: '提交', pattern: /提交|submit/i },
365
+ { label: 'OK', pattern: /\bOK\b|确定/ },
366
+ { label: 'Cancel', pattern: /cancel|取消/i },
367
+ { label: '编辑', pattern: /编辑/ },
368
+ ]
369
+ return matchers.find((matcher) => {
370
+ return matcher.pattern.test(text)
371
+ })?.label
372
+ }
373
+
374
+ function nodeLabelFromStructure(state: ReplayDomState, nodeId: number): string | undefined {
375
+ const ancestors = nodeAndAncestors(state, nodeId, 8)
376
+ const classText = ancestors
377
+ .map((node) => {
378
+ return node.attributes.class || ''
379
+ })
380
+ .join(' ')
381
+ const inModal = classText.includes('designer-modal')
382
+ if (classText.includes('designer-formily-array-base-addition')) {
383
+ return 'Add entry'
384
+ }
385
+ if (inModal && classText.includes('designer-btn-dangerous')) {
386
+ return '重新编辑'
387
+ }
388
+ if (inModal && classText.includes('designer-btn-primary')) {
389
+ return 'OK'
390
+ }
391
+ return undefined
392
+ }
393
+
394
+ function nodeLabelFromAttributes(node: ReplayDomNode): string | undefined {
395
+ return (
396
+ optionalString(node.attributes['aria-label']) ||
397
+ optionalString(node.attributes.title) ||
398
+ optionalString(node.attributes.placeholder) ||
399
+ optionalString(node.attributes.name)
400
+ )
401
+ }
402
+
403
+ function nodeLabel(state: ReplayDomState, nodeId: number): string | undefined {
404
+ const structuredLabel = nodeLabelFromStructure(state, nodeId)
405
+ if (structuredLabel) {
406
+ return structuredLabel
407
+ }
408
+ const current = state.nodes.get(nodeId)
409
+ const currentAttributeLabel = current ? nodeLabelFromAttributes(current) : undefined
410
+ if (currentAttributeLabel) {
411
+ return currentAttributeLabel
412
+ }
413
+ const candidates = nodeAndAncestors(state, nodeId, 8)
414
+ .flatMap((node) => {
415
+ return [nodeLabelFromAttributes(node), nodeText(state, node.id)]
416
+ })
417
+ .filter((text): text is string => {
418
+ return Boolean(text)
419
+ })
420
+ const actionLabel = candidates
421
+ .map((text) => {
422
+ return actionLabelFromText(text)
423
+ })
424
+ .find((label) => {
425
+ return Boolean(label)
426
+ })
427
+ if (actionLabel) {
428
+ return actionLabel
429
+ }
430
+ return candidates.find((text) => {
431
+ return text.length <= 120 && !isNoisyText(text)
432
+ })
433
+ }
434
+
435
+ function attrSelector(name: string, value: string): string {
436
+ return `[${name}=${JSON.stringify(value)}]`
437
+ }
438
+
439
+ function selectorHints(node: ReplayDomNode): string[] {
440
+ const tagName = node.tagName || ''
441
+ const className = node.attributes.class || ''
442
+ const classHints = className
443
+ .split(/\s+/)
444
+ .filter((classPart) => {
445
+ return classPart === 'designer-formily-array-base-addition' || classPart === 'designer-input'
446
+ })
447
+ .map((classPart) => {
448
+ return tagName ? `${tagName}.${classPart}` : `.${classPart}`
449
+ })
450
+ return uniqueStrings([
451
+ node.attributes.id ? `#${node.attributes.id}` : '',
452
+ node.attributes['data-testid'] ? attrSelector('data-testid', node.attributes['data-testid']) : '',
453
+ node.attributes['data-test'] ? attrSelector('data-test', node.attributes['data-test']) : '',
454
+ node.attributes.name && tagName ? `${tagName}${attrSelector('name', node.attributes.name)}` : '',
455
+ node.attributes.placeholder && tagName ? `${tagName}${attrSelector('placeholder', node.attributes.placeholder)}` : '',
456
+ node.attributes['aria-label'] && tagName ? `${tagName}${attrSelector('aria-label', node.attributes['aria-label'])}` : '',
457
+ ...classHints,
458
+ ])
459
+ }
460
+
461
+ function nodeTypeName(type: ReplayNodeType): string {
462
+ if (type === 0) {
463
+ return 'document'
464
+ }
465
+ if (type === 1) {
466
+ return 'doctype'
467
+ }
468
+ if (type === 2) {
469
+ return 'element'
470
+ }
471
+ if (type === 3) {
472
+ return 'text'
473
+ }
474
+ if (type === 4) {
475
+ return 'cdata'
476
+ }
477
+ return 'comment'
478
+ }
479
+
480
+ function summarizeNode(state: ReplayDomState, nodeId: number): ReplayAiNodeSummary | undefined {
481
+ const node = state.nodes.get(nodeId)
482
+ if (!node) {
483
+ return undefined
484
+ }
485
+ const text = nodeText(state, nodeId)
486
+ const ancestors = nodeAndAncestors(state, nodeId, 8).slice(1)
487
+ return {
488
+ id: node.id,
489
+ type: nodeTypeName(node.type),
490
+ tagName: node.tagName,
491
+ text: text && text.length <= 240 && !isNoisyText(text) ? text : undefined,
492
+ label: nodeLabel(state, nodeId),
493
+ role: optionalString(node.attributes.role),
494
+ name: optionalString(node.attributes.name),
495
+ placeholder: optionalString(node.attributes.placeholder),
496
+ className: optionalString(node.attributes.class),
497
+ selectorHints: selectorHints(node),
498
+ ancestorText: uniqueStrings(
499
+ ancestors
500
+ .map((ancestor) => {
501
+ return nodeText(state, ancestor.id)
502
+ })
503
+ .filter((ancestorText) => {
504
+ return ancestorText.length > 0 && ancestorText.length <= 160 && !isNoisyText(ancestorText)
505
+ }),
506
+ ).slice(0, 6),
507
+ }
508
+ }
509
+
510
+ function isInteractiveNode(node: ReplayDomNode): boolean {
511
+ const tagName = node.tagName || ''
512
+ const role = node.attributes.role || ''
513
+ const className = node.attributes.class || ''
514
+ return (
515
+ ['button', 'a', 'input', 'textarea', 'select', 'option'].includes(tagName) ||
516
+ ['button', 'textbox', 'checkbox', 'combobox', 'menuitem', 'tab'].includes(role) ||
517
+ className.includes('designer-formily-array-base-addition')
518
+ )
519
+ }
520
+
521
+ function getFirstTimestamp(events: RrwebEvent[]): number | undefined {
522
+ return events
523
+ .map((event) => {
524
+ return getEventTimestamp(event)
525
+ })
526
+ .find((timestamp) => {
527
+ return timestamp !== undefined
528
+ })
529
+ }
530
+
531
+ function buildFields(actions: ReplayAiAction[]): ReplayAiField[] {
532
+ return Object.values(
533
+ actions
534
+ .filter((action) => {
535
+ return action.kind === 'input'
536
+ })
537
+ .reduce<Record<string, ReplayAiField>>((fields, action) => {
538
+ const key = action.nodeId === undefined ? action.label : `rrweb:${action.nodeId}`
539
+ const existing = fields[key]
540
+ return {
541
+ ...fields,
542
+ [key]: {
543
+ key,
544
+ label: action.node?.label || action.label,
545
+ nodeId: action.nodeId,
546
+ selectorHints: uniqueStrings([...(existing?.selectorHints || []), ...(action.node?.selectorHints || [])]),
547
+ value: action.value ?? existing?.value,
548
+ checked: action.checked ?? existing?.checked,
549
+ actionCount: (existing?.actionCount || 0) + 1,
550
+ updatedAt: action.timestamp,
551
+ },
552
+ }
553
+ }, {}),
554
+ ).sort((a, b) => {
555
+ return a.updatedAt - b.updatedAt
556
+ })
557
+ }
558
+
559
+ function extractUrlFromEvent(event: RrwebEvent): string | undefined {
560
+ const data = getEventData(event)
561
+ return optionalString(data.href) || optionalString(data.url)
562
+ }
563
+
564
+ function readAnnotationRect(value: unknown): ReplayAiAnnotationRect | undefined {
565
+ if (!isRecord(value)) {
566
+ return undefined
567
+ }
568
+ const x = numberValue(value.x)
569
+ const y = numberValue(value.y)
570
+ const width = numberValue(value.width)
571
+ const height = numberValue(value.height)
572
+ if (x === undefined || y === undefined || width === undefined || height === undefined) {
573
+ return undefined
574
+ }
575
+ return { x, y, width, height }
576
+ }
577
+
578
+ function readAnnotationTarget(value: unknown): ReplayAiAnnotationTarget | undefined {
579
+ if (!isRecord(value)) {
580
+ return undefined
581
+ }
582
+ const selectorHints = stringArrayValue(value.selectorHints)
583
+ const target: ReplayAiAnnotationTarget = {
584
+ tagName: optionalString(value.tagName),
585
+ label: optionalString(value.label),
586
+ text: optionalString(value.text),
587
+ role: optionalString(value.role),
588
+ name: optionalString(value.name),
589
+ placeholder: optionalString(value.placeholder),
590
+ selectorHints,
591
+ rect: readAnnotationRect(value.rect),
592
+ }
593
+ if (
594
+ !target.tagName &&
595
+ !target.label &&
596
+ !target.text &&
597
+ !target.role &&
598
+ !target.name &&
599
+ !target.placeholder &&
600
+ target.selectorHints.length === 0 &&
601
+ !target.rect
602
+ ) {
603
+ return undefined
604
+ }
605
+ return target
606
+ }
607
+
608
+ function readAnnotationEvent(options: {
609
+ data: Record<string, unknown>
610
+ timestamp: number
611
+ relativeTime?: number
612
+ }): ReplayAiAnnotation | undefined {
613
+ if (optionalString(options.data.tag) !== 'playwriter.annotation') {
614
+ return undefined
615
+ }
616
+ const payload = isRecord(options.data.payload) ? options.data.payload : {}
617
+ const id = optionalString(payload.id)
618
+ const text = normalizeText(optionalString(payload.text))
619
+ if (!id || !text) {
620
+ return undefined
621
+ }
622
+ return {
623
+ id,
624
+ text,
625
+ timestamp: numberValue(payload.timestamp) || options.timestamp,
626
+ relativeTime: options.relativeTime,
627
+ url: optionalString(payload.url),
628
+ target: readAnnotationTarget(payload.target),
629
+ }
630
+ }
631
+
632
+ function readAnnotationDeleteEvent(options: { data: Record<string, unknown> }): string | undefined {
633
+ if (optionalString(options.data.tag) !== 'playwriter.annotation.delete') {
634
+ return undefined
635
+ }
636
+ const payload = isRecord(options.data.payload) ? options.data.payload : {}
637
+ return optionalString(payload.id)
638
+ }
639
+
640
+ export function buildReplayAiIndex(options: {
641
+ replayId: string
642
+ url?: string
643
+ events: RrwebEvent[]
644
+ }): ReplayAiIndex {
645
+ const state: ReplayDomState = { nodes: new Map<number, ReplayDomNode>() }
646
+ const firstTimestamp = getFirstTimestamp(options.events)
647
+ const stats: ReplayAiIndexStats = {
648
+ eventCount: options.events.length,
649
+ fullSnapshotCount: 0,
650
+ mutationEventCount: 0,
651
+ clickEventCount: 0,
652
+ inputEventCount: 0,
653
+ annotationCount: 0,
654
+ }
655
+ const warnings: string[] = []
656
+ const actions: ReplayAiAction[] = []
657
+ const annotationsById: Map<string, ReplayAiAnnotation> = new Map()
658
+ let url = options.url
659
+
660
+ options.events.forEach((event) => {
661
+ const eventType = getEventType(event)
662
+ const data = getEventData(event)
663
+ if (!url) {
664
+ url = extractUrlFromEvent(event)
665
+ }
666
+ if (eventType === 2) {
667
+ stats.fullSnapshotCount += 1
668
+ addReplayNode(state, data.node)
669
+ return
670
+ }
671
+ const timestamp = getEventTimestamp(event)
672
+ const relativeTime = firstTimestamp === undefined || timestamp === undefined ? undefined : timestamp - firstTimestamp
673
+ if (eventType === 5) {
674
+ if (timestamp === undefined) {
675
+ warnings.push('Ignored rrweb custom event without timestamp.')
676
+ return
677
+ }
678
+ const deletedAnnotationId = readAnnotationDeleteEvent({ data })
679
+ if (deletedAnnotationId) {
680
+ annotationsById.delete(deletedAnnotationId)
681
+ stats.annotationCount = annotationsById.size
682
+ return
683
+ }
684
+ const annotation = readAnnotationEvent({ data, timestamp, relativeTime })
685
+ if (annotation) {
686
+ annotationsById.set(annotation.id, annotation)
687
+ stats.annotationCount = annotationsById.size
688
+ }
689
+ return
690
+ }
691
+ if (eventType !== 3) {
692
+ return
693
+ }
694
+
695
+ const source = numberValue(data.source)
696
+ if (source === 0) {
697
+ stats.mutationEventCount += 1
698
+ applyMutationEvent(state, data)
699
+ return
700
+ }
701
+
702
+ if (timestamp === undefined) {
703
+ warnings.push('Ignored rrweb event without timestamp.')
704
+ return
705
+ }
706
+
707
+ if (source === 5) {
708
+ stats.inputEventCount += 1
709
+ const nodeId = numberValue(data.id)
710
+ const value = optionalString(data.text) || optionalString(data.value) || ''
711
+ const checked = typeof data.isChecked === 'boolean' ? data.isChecked : undefined
712
+ const node = nodeId === undefined ? undefined : state.nodes.get(nodeId)
713
+ if (node) {
714
+ node.attributes = {
715
+ ...node.attributes,
716
+ value,
717
+ }
718
+ }
719
+ const summary = nodeId === undefined ? undefined : summarizeNode(state, nodeId)
720
+ actions.push({
721
+ kind: 'input',
722
+ timestamp,
723
+ relativeTime,
724
+ nodeId,
725
+ label:
726
+ summary?.label ||
727
+ summary?.placeholder ||
728
+ summary?.name ||
729
+ summary?.selectorHints[0] ||
730
+ (nodeId === undefined ? 'input' : `DOM node #${nodeId}`),
731
+ value,
732
+ checked,
733
+ node: summary,
734
+ })
735
+ return
736
+ }
737
+
738
+ if (source === 2 && data.type === 2) {
739
+ stats.clickEventCount += 1
740
+ const nodeId = numberValue(data.id)
741
+ const summary = nodeId === undefined ? undefined : summarizeNode(state, nodeId)
742
+ actions.push({
743
+ kind: 'click',
744
+ timestamp,
745
+ relativeTime,
746
+ nodeId,
747
+ label:
748
+ summary?.label ||
749
+ summary?.text ||
750
+ summary?.selectorHints[0] ||
751
+ (nodeId === undefined ? 'click' : `DOM node #${nodeId}`),
752
+ node: summary,
753
+ })
754
+ }
755
+ })
756
+
757
+ const interactiveElements = Array.from(state.nodes.values())
758
+ .filter(isInteractiveNode)
759
+ .map((node) => {
760
+ return summarizeNode(state, node.id)
761
+ })
762
+ .filter((summary): summary is ReplayAiNodeSummary => {
763
+ return Boolean(summary)
764
+ })
765
+ .filter((summary) => {
766
+ return Boolean(summary.label || summary.text || summary.selectorHints.length > 0)
767
+ })
768
+ .slice(0, MAX_INTERACTIVE_ELEMENTS)
769
+ const pageText = uniqueStrings(
770
+ Array.from(state.nodes.values())
771
+ .map((node) => {
772
+ return nodeText(state, node.id)
773
+ })
774
+ .filter((text) => {
775
+ return text.length > 0 && text.length <= 160 && !isNoisyText(text)
776
+ }),
777
+ ).slice(0, MAX_PAGE_TEXTS)
778
+
779
+ return {
780
+ schemaVersion: 1,
781
+ replayId: options.replayId,
782
+ url,
783
+ generatedAt: Date.now(),
784
+ stats,
785
+ actions: actions.slice(0, MAX_AI_ACTIONS),
786
+ fields: buildFields(actions),
787
+ annotations: Array.from(annotationsById.values()),
788
+ interactiveElements,
789
+ pageText,
790
+ warnings: uniqueStrings(warnings),
791
+ }
792
+ }
793
+
794
+ export function createReplayAiIndexFromRecording(replayId: string): ReplayAiIndex {
795
+ const replay = getSavedRrwebRecordingWithEvents(replayId)
796
+ if (!replay) {
797
+ throw new Error(`Replay recording not found: ${replayId}`)
798
+ }
799
+ return buildReplayAiIndex({
800
+ replayId,
801
+ url: replay.recording.url,
802
+ events: replay.events,
803
+ })
804
+ }
805
+
806
+ function isSavedReplayAiIndex(value: unknown): value is SavedReplayAiIndex {
807
+ if (!isRecord(value)) {
808
+ return false
809
+ }
810
+ return (
811
+ typeof value.id === 'string' &&
812
+ typeof value.replayId === 'string' &&
813
+ typeof value.path === 'string' &&
814
+ typeof value.generatedAt === 'number' &&
815
+ typeof value.size === 'number' &&
816
+ typeof value.actionCount === 'number' &&
817
+ typeof value.fieldCount === 'number' &&
818
+ (value.url === undefined || typeof value.url === 'string')
819
+ )
820
+ }
821
+
822
+ function readSavedReplayAiIndexes(): SavedReplayAiIndex[] {
823
+ const indexPath = getReplayAiIndexesIndexPath()
824
+ if (!fs.existsSync(indexPath)) {
825
+ return []
826
+ }
827
+ try {
828
+ const parsed = JSON.parse(fs.readFileSync(indexPath, 'utf-8')) as unknown
829
+ if (!Array.isArray(parsed)) {
830
+ return []
831
+ }
832
+ return parsed.filter(isSavedReplayAiIndex)
833
+ } catch {
834
+ return []
835
+ }
836
+ }
837
+
838
+ function writeSavedReplayAiIndexes(indexes: SavedReplayAiIndex[]): void {
839
+ const dir = getReplayAiIndexesDir()
840
+ if (!fs.existsSync(dir)) {
841
+ fs.mkdirSync(dir, { recursive: true })
842
+ }
843
+ fs.writeFileSync(getReplayAiIndexesIndexPath(), `${JSON.stringify(indexes, null, 2)}\n`)
844
+ }
845
+
846
+ export function saveReplayAiIndex(index: ReplayAiIndex): SavedReplayAiIndex {
847
+ const outputPath = getReplayAiIndexPath(index.replayId)
848
+ const dir = path.dirname(outputPath)
849
+ if (!fs.existsSync(dir)) {
850
+ fs.mkdirSync(dir, { recursive: true })
851
+ }
852
+ const payload = `${JSON.stringify(index, null, 2)}\n`
853
+ fs.writeFileSync(outputPath, payload)
854
+ const saved: SavedReplayAiIndex = {
855
+ id: index.replayId,
856
+ replayId: index.replayId,
857
+ path: outputPath,
858
+ generatedAt: index.generatedAt,
859
+ size: Buffer.byteLength(payload),
860
+ actionCount: index.actions.length,
861
+ fieldCount: index.fields.length,
862
+ url: index.url,
863
+ }
864
+ const nextIndexes = [
865
+ saved,
866
+ ...readSavedReplayAiIndexes().filter((existing) => {
867
+ return existing.id !== saved.id
868
+ }),
869
+ ]
870
+ writeSavedReplayAiIndexes(nextIndexes.slice(0, 500))
871
+ return saved
872
+ }