@agentrhq/webcmd 0.1.1 → 0.1.2

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 (415) hide show
  1. package/README.md +317 -24
  2. package/dist/src/adapter-shadow.d.ts +11 -0
  3. package/dist/src/adapter-shadow.js +72 -0
  4. package/dist/src/adapter-shadow.test.d.ts +1 -0
  5. package/dist/src/adapter-shadow.test.js +49 -0
  6. package/dist/src/adapter-source.d.ts +11 -0
  7. package/dist/src/adapter-source.js +24 -0
  8. package/dist/src/adapter-source.test.d.ts +1 -0
  9. package/dist/src/adapter-source.test.js +29 -0
  10. package/dist/src/brand.d.ts +9 -0
  11. package/dist/src/brand.js +9 -0
  12. package/dist/src/brand.test.d.ts +1 -0
  13. package/dist/src/brand.test.js +15 -0
  14. package/dist/src/browser/analyze.d.ts +118 -0
  15. package/dist/src/browser/analyze.js +405 -0
  16. package/dist/src/browser/analyze.test.d.ts +1 -0
  17. package/dist/src/browser/analyze.test.js +209 -0
  18. package/dist/src/browser/article-extract.d.ts +57 -0
  19. package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
  20. package/dist/src/browser/article-extract.e2e.test.js +105 -0
  21. package/dist/src/browser/article-extract.js +169 -0
  22. package/dist/src/browser/article-extract.test.d.ts +1 -0
  23. package/dist/src/browser/article-extract.test.js +94 -0
  24. package/dist/src/browser/ax-snapshot.d.ts +37 -0
  25. package/dist/src/browser/ax-snapshot.js +217 -0
  26. package/dist/src/browser/ax-snapshot.test.d.ts +1 -0
  27. package/dist/src/browser/ax-snapshot.test.js +91 -0
  28. package/dist/src/browser/base-page.d.ts +151 -0
  29. package/dist/src/browser/base-page.js +1060 -0
  30. package/dist/src/browser/base-page.test.d.ts +1 -0
  31. package/dist/src/browser/base-page.test.js +803 -0
  32. package/dist/src/browser/bridge-readiness.d.ts +14 -0
  33. package/dist/src/browser/bridge-readiness.js +24 -0
  34. package/dist/src/browser/bridge-readiness.test.d.ts +1 -0
  35. package/dist/src/browser/bridge-readiness.test.js +58 -0
  36. package/dist/src/browser/bridge.d.ts +26 -0
  37. package/dist/src/browser/bridge.js +58 -0
  38. package/dist/src/browser/cdp-click-fixture.test.d.ts +1 -0
  39. package/dist/src/browser/cdp-click-fixture.test.js +87 -0
  40. package/dist/src/browser/cdp.d.ts +46 -0
  41. package/dist/src/browser/cdp.js +536 -0
  42. package/dist/src/browser/cdp.test.d.ts +1 -0
  43. package/dist/src/browser/cdp.test.js +79 -0
  44. package/dist/src/browser/compound.d.ts +59 -0
  45. package/dist/src/browser/compound.js +112 -0
  46. package/dist/src/browser/compound.test.d.ts +1 -0
  47. package/dist/src/browser/compound.test.js +175 -0
  48. package/dist/src/browser/config.d.ts +2 -0
  49. package/dist/src/browser/config.js +14 -0
  50. package/dist/src/browser/daemon-client.d.ts +32 -0
  51. package/dist/src/browser/daemon-client.js +135 -0
  52. package/dist/src/browser/daemon-client.test.d.ts +1 -0
  53. package/dist/src/browser/daemon-client.test.js +326 -0
  54. package/dist/src/browser/daemon-lifecycle.d.ts +37 -0
  55. package/dist/src/browser/daemon-lifecycle.js +147 -0
  56. package/dist/src/browser/daemon-transport.d.ts +54 -0
  57. package/dist/src/browser/daemon-transport.js +68 -0
  58. package/dist/src/browser/daemon-version.d.ts +4 -0
  59. package/dist/src/browser/daemon-version.js +12 -0
  60. package/dist/src/browser/dom-helpers.d.ts +41 -0
  61. package/dist/src/browser/dom-helpers.js +239 -0
  62. package/dist/src/browser/dom-helpers.test.d.ts +1 -0
  63. package/dist/src/browser/dom-helpers.test.js +106 -0
  64. package/dist/src/browser/dom-snapshot.d.ts +93 -0
  65. package/dist/src/browser/dom-snapshot.js +894 -0
  66. package/dist/src/browser/dom-snapshot.test.d.ts +11 -0
  67. package/dist/src/browser/dom-snapshot.test.js +313 -0
  68. package/dist/src/browser/errors.d.ts +38 -0
  69. package/dist/src/browser/errors.js +79 -0
  70. package/dist/src/browser/errors.test.d.ts +1 -0
  71. package/dist/src/browser/errors.test.js +60 -0
  72. package/dist/src/browser/extract.d.ts +69 -0
  73. package/dist/src/browser/extract.js +132 -0
  74. package/dist/src/browser/extract.test.d.ts +1 -0
  75. package/dist/src/browser/extract.test.js +129 -0
  76. package/dist/src/browser/find.d.ts +84 -0
  77. package/dist/src/browser/find.js +398 -0
  78. package/dist/src/browser/find.test.d.ts +1 -0
  79. package/dist/src/browser/find.test.js +180 -0
  80. package/dist/src/browser/html-tree.d.ts +75 -0
  81. package/dist/src/browser/html-tree.js +112 -0
  82. package/dist/src/browser/html-tree.test.d.ts +1 -0
  83. package/dist/src/browser/html-tree.test.js +181 -0
  84. package/dist/src/browser/index.d.ts +14 -0
  85. package/dist/src/browser/index.js +12 -0
  86. package/dist/src/browser/network-cache.d.ts +49 -0
  87. package/dist/src/browser/network-cache.js +78 -0
  88. package/dist/src/browser/network-cache.test.d.ts +1 -0
  89. package/dist/src/browser/network-cache.test.js +75 -0
  90. package/dist/src/browser/network-interceptor.d.ts +11 -0
  91. package/dist/src/browser/network-interceptor.js +11 -0
  92. package/dist/src/browser/network-key.d.ts +22 -0
  93. package/dist/src/browser/network-key.js +66 -0
  94. package/dist/src/browser/network-key.test.d.ts +1 -0
  95. package/dist/src/browser/network-key.test.js +49 -0
  96. package/dist/src/browser/page.d.ts +83 -0
  97. package/dist/src/browser/page.js +453 -0
  98. package/dist/src/browser/page.test.d.ts +1 -0
  99. package/dist/src/browser/page.test.js +406 -0
  100. package/dist/src/browser/profile.d.ts +14 -0
  101. package/dist/src/browser/profile.js +86 -0
  102. package/dist/src/browser/protocol.d.ts +63 -0
  103. package/dist/src/browser/protocol.js +1 -0
  104. package/dist/src/browser/runtime/local-cloak/actions.d.ts +3 -0
  105. package/dist/src/browser/runtime/local-cloak/actions.js +240 -0
  106. package/dist/src/browser/runtime/local-cloak/downloads.d.ts +3 -0
  107. package/dist/src/browser/runtime/local-cloak/downloads.js +28 -0
  108. package/dist/src/browser/runtime/local-cloak/network.d.ts +28 -0
  109. package/dist/src/browser/runtime/local-cloak/network.js +120 -0
  110. package/dist/src/browser/runtime/local-cloak/network.test.d.ts +1 -0
  111. package/dist/src/browser/runtime/local-cloak/network.test.js +124 -0
  112. package/dist/src/browser/runtime/local-cloak/profiles.d.ts +6 -0
  113. package/dist/src/browser/runtime/local-cloak/profiles.js +17 -0
  114. package/dist/src/browser/runtime/local-cloak/profiles.test.d.ts +1 -0
  115. package/dist/src/browser/runtime/local-cloak/profiles.test.js +19 -0
  116. package/dist/src/browser/runtime/local-cloak/provider.d.ts +15 -0
  117. package/dist/src/browser/runtime/local-cloak/provider.js +27 -0
  118. package/dist/src/browser/runtime/local-cloak/provider.test.d.ts +1 -0
  119. package/dist/src/browser/runtime/local-cloak/provider.test.js +244 -0
  120. package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +77 -0
  121. package/dist/src/browser/runtime/local-cloak/session-manager.js +280 -0
  122. package/dist/src/browser/runtime/local-cloak/session-manager.test.d.ts +1 -0
  123. package/dist/src/browser/runtime/local-cloak/session-manager.test.js +92 -0
  124. package/dist/src/browser/runtime/provider.d.ts +9 -0
  125. package/dist/src/browser/runtime/provider.js +1 -0
  126. package/dist/src/browser/runtime/provider.test.d.ts +2 -0
  127. package/dist/src/browser/runtime/provider.test.js +49 -0
  128. package/dist/src/browser/shape-filter.d.ts +52 -0
  129. package/dist/src/browser/shape-filter.js +101 -0
  130. package/dist/src/browser/shape-filter.test.d.ts +1 -0
  131. package/dist/src/browser/shape-filter.test.js +101 -0
  132. package/dist/src/browser/shape.d.ts +23 -0
  133. package/dist/src/browser/shape.js +95 -0
  134. package/dist/src/browser/shape.test.d.ts +1 -0
  135. package/dist/src/browser/shape.test.js +82 -0
  136. package/dist/src/browser/stealth.d.ts +11 -0
  137. package/dist/src/browser/stealth.js +359 -0
  138. package/dist/src/browser/stealth.test.d.ts +1 -0
  139. package/dist/src/browser/stealth.test.js +134 -0
  140. package/dist/src/browser/tabs.d.ts +13 -0
  141. package/dist/src/browser/tabs.js +70 -0
  142. package/dist/src/browser/target-errors.d.ts +39 -0
  143. package/dist/src/browser/target-errors.js +45 -0
  144. package/dist/src/browser/target-errors.test.d.ts +1 -0
  145. package/dist/src/browser/target-errors.test.js +94 -0
  146. package/dist/src/browser/target-resolver.d.ts +158 -0
  147. package/dist/src/browser/target-resolver.js +570 -0
  148. package/dist/src/browser/target-resolver.test.d.ts +1 -0
  149. package/dist/src/browser/target-resolver.test.js +118 -0
  150. package/dist/src/browser/utils.d.ts +20 -0
  151. package/dist/src/browser/utils.js +64 -0
  152. package/dist/src/browser/utils.test.d.ts +1 -0
  153. package/dist/src/browser/utils.test.js +29 -0
  154. package/dist/src/browser/verify-fixture.d.ts +65 -0
  155. package/dist/src/browser/verify-fixture.js +318 -0
  156. package/dist/src/browser/verify-fixture.test.d.ts +1 -0
  157. package/dist/src/browser/verify-fixture.test.js +219 -0
  158. package/dist/src/browser/visual-refs.d.ts +11 -0
  159. package/dist/src/browser/visual-refs.js +108 -0
  160. package/dist/src/browser.test.d.ts +1 -0
  161. package/dist/src/browser.test.js +297 -0
  162. package/dist/src/build-manifest.d.ts +103 -0
  163. package/dist/src/build-manifest.js +363 -0
  164. package/dist/src/build-manifest.test.d.ts +1 -0
  165. package/dist/src/build-manifest.test.js +341 -0
  166. package/dist/src/capabilityRouting.d.ts +19 -0
  167. package/dist/src/capabilityRouting.js +56 -0
  168. package/dist/src/capabilityRouting.test.d.ts +1 -0
  169. package/dist/src/capabilityRouting.test.js +63 -0
  170. package/dist/src/cli-argv-preprocess.d.ts +63 -0
  171. package/dist/src/cli-argv-preprocess.js +303 -0
  172. package/dist/src/cli-argv-preprocess.test.d.ts +1 -0
  173. package/dist/src/cli-argv-preprocess.test.js +304 -0
  174. package/dist/src/cli.d.ts +79 -0
  175. package/dist/src/cli.js +3350 -0
  176. package/dist/src/cli.test.d.ts +1 -0
  177. package/dist/src/cli.test.js +3041 -0
  178. package/dist/src/commanderAdapter.d.ts +21 -0
  179. package/dist/src/commanderAdapter.js +208 -0
  180. package/dist/src/commanderAdapter.test.d.ts +1 -0
  181. package/dist/src/commanderAdapter.test.js +352 -0
  182. package/dist/src/commands/auth.d.ts +40 -0
  183. package/dist/src/commands/auth.js +434 -0
  184. package/dist/src/commands/auth.test.d.ts +1 -0
  185. package/dist/src/commands/auth.test.js +252 -0
  186. package/dist/src/commands/daemon.d.ts +9 -0
  187. package/dist/src/commands/daemon.js +91 -0
  188. package/dist/src/commands/daemon.test.d.ts +1 -0
  189. package/dist/src/commands/daemon.test.js +293 -0
  190. package/dist/src/completion-fast.d.ts +22 -0
  191. package/dist/src/completion-fast.js +88 -0
  192. package/dist/src/completion-shared.d.ts +13 -0
  193. package/dist/src/completion-shared.js +61 -0
  194. package/dist/src/completion.d.ts +21 -0
  195. package/dist/src/completion.js +68 -0
  196. package/dist/src/completion.test.d.ts +1 -0
  197. package/dist/src/completion.test.js +24 -0
  198. package/dist/src/constants.d.ts +18 -0
  199. package/dist/src/constants.js +41 -0
  200. package/dist/src/convention-audit.d.ts +50 -0
  201. package/dist/src/convention-audit.js +553 -0
  202. package/dist/src/convention-audit.test.d.ts +1 -0
  203. package/dist/src/convention-audit.test.js +255 -0
  204. package/dist/src/daemon/server.d.ts +13 -0
  205. package/dist/src/daemon/server.js +185 -0
  206. package/dist/src/daemon/server.test.d.ts +1 -0
  207. package/dist/src/daemon/server.test.js +132 -0
  208. package/dist/src/daemon-utils.d.ts +18 -0
  209. package/dist/src/daemon-utils.js +37 -0
  210. package/dist/src/daemon.d.ts +1 -0
  211. package/dist/src/daemon.js +27 -0
  212. package/dist/src/daemon.test.d.ts +1 -0
  213. package/dist/src/daemon.test.js +60 -0
  214. package/dist/src/discovery.d.ts +46 -0
  215. package/dist/src/discovery.js +341 -0
  216. package/dist/src/doctor.d.ts +39 -0
  217. package/dist/src/doctor.js +168 -0
  218. package/dist/src/doctor.test.d.ts +1 -0
  219. package/dist/src/doctor.test.js +302 -0
  220. package/dist/src/download/article-download.d.ts +72 -0
  221. package/dist/src/download/article-download.js +305 -0
  222. package/dist/src/download/article-download.test.d.ts +1 -0
  223. package/dist/src/download/article-download.test.js +235 -0
  224. package/dist/src/download/index.d.ts +71 -0
  225. package/dist/src/download/index.js +363 -0
  226. package/dist/src/download/index.test.d.ts +1 -0
  227. package/dist/src/download/index.test.js +174 -0
  228. package/dist/src/download/media-download.d.ts +49 -0
  229. package/dist/src/download/media-download.js +127 -0
  230. package/dist/src/download/media-download.test.d.ts +1 -0
  231. package/dist/src/download/media-download.test.js +112 -0
  232. package/dist/src/download/progress.d.ts +36 -0
  233. package/dist/src/download/progress.js +120 -0
  234. package/dist/src/download/progress.test.d.ts +1 -0
  235. package/dist/src/download/progress.test.js +36 -0
  236. package/dist/src/electron-apps.d.ts +31 -0
  237. package/dist/src/electron-apps.js +91 -0
  238. package/dist/src/electron-apps.test.d.ts +1 -0
  239. package/dist/src/electron-apps.test.js +76 -0
  240. package/dist/src/engine.test.d.ts +1 -0
  241. package/dist/src/engine.test.js +312 -0
  242. package/dist/src/errors.d.ts +114 -0
  243. package/dist/src/errors.js +174 -0
  244. package/dist/src/errors.test.d.ts +1 -0
  245. package/dist/src/errors.test.js +109 -0
  246. package/dist/src/execution.d.ts +24 -0
  247. package/dist/src/execution.js +509 -0
  248. package/dist/src/execution.test.d.ts +1 -0
  249. package/dist/src/execution.test.js +645 -0
  250. package/dist/src/external.d.ts +49 -0
  251. package/dist/src/external.js +211 -0
  252. package/dist/src/external.test.d.ts +1 -0
  253. package/dist/src/external.test.js +110 -0
  254. package/dist/src/help.d.ts +88 -0
  255. package/dist/src/help.js +556 -0
  256. package/dist/src/help.test.d.ts +1 -0
  257. package/dist/src/help.test.js +58 -0
  258. package/dist/src/hooks.d.ts +46 -0
  259. package/dist/src/hooks.js +58 -0
  260. package/dist/src/hooks.test.d.ts +4 -0
  261. package/dist/src/hooks.test.js +92 -0
  262. package/dist/src/interceptor.d.ts +44 -0
  263. package/dist/src/interceptor.js +183 -0
  264. package/dist/src/interceptor.test.d.ts +4 -0
  265. package/dist/src/interceptor.test.js +81 -0
  266. package/dist/src/launcher.d.ts +41 -0
  267. package/dist/src/launcher.js +226 -0
  268. package/dist/src/launcher.test.d.ts +1 -0
  269. package/dist/src/launcher.test.js +153 -0
  270. package/dist/src/logger.d.ts +26 -0
  271. package/dist/src/logger.js +49 -0
  272. package/dist/src/main.d.ts +5 -0
  273. package/dist/src/main.js +177 -0
  274. package/dist/src/manifest-types.d.ts +43 -0
  275. package/dist/src/manifest-types.js +9 -0
  276. package/dist/src/node-network.d.ts +10 -0
  277. package/dist/src/node-network.js +174 -0
  278. package/dist/src/node-network.test.d.ts +1 -0
  279. package/dist/src/node-network.test.js +55 -0
  280. package/dist/src/observation/artifact.d.ts +16 -0
  281. package/dist/src/observation/artifact.js +260 -0
  282. package/dist/src/observation/artifact.test.d.ts +1 -0
  283. package/dist/src/observation/artifact.test.js +121 -0
  284. package/dist/src/observation/events.d.ts +89 -0
  285. package/dist/src/observation/events.js +1 -0
  286. package/dist/src/observation/index.d.ts +7 -0
  287. package/dist/src/observation/index.js +7 -0
  288. package/dist/src/observation/manager.d.ts +9 -0
  289. package/dist/src/observation/manager.js +27 -0
  290. package/dist/src/observation/manager.test.d.ts +1 -0
  291. package/dist/src/observation/manager.test.js +13 -0
  292. package/dist/src/observation/redaction.d.ts +11 -0
  293. package/dist/src/observation/redaction.js +81 -0
  294. package/dist/src/observation/redaction.test.d.ts +1 -0
  295. package/dist/src/observation/redaction.test.js +32 -0
  296. package/dist/src/observation/retention.d.ts +32 -0
  297. package/dist/src/observation/retention.js +160 -0
  298. package/dist/src/observation/retention.test.d.ts +1 -0
  299. package/dist/src/observation/retention.test.js +118 -0
  300. package/dist/src/observation/ring-buffer.d.ts +22 -0
  301. package/dist/src/observation/ring-buffer.js +45 -0
  302. package/dist/src/observation/ring-buffer.test.d.ts +1 -0
  303. package/dist/src/observation/ring-buffer.test.js +22 -0
  304. package/dist/src/observation/session.d.ts +25 -0
  305. package/dist/src/observation/session.js +50 -0
  306. package/dist/src/output.d.ts +14 -0
  307. package/dist/src/output.js +152 -0
  308. package/dist/src/output.test.d.ts +1 -0
  309. package/dist/src/output.test.js +53 -0
  310. package/dist/src/package-exports.test.d.ts +1 -0
  311. package/dist/src/package-exports.test.js +112 -0
  312. package/dist/src/package-paths.d.ts +8 -0
  313. package/dist/src/package-paths.js +41 -0
  314. package/dist/src/pipeline/executor.d.ts +11 -0
  315. package/dist/src/pipeline/executor.js +93 -0
  316. package/dist/src/pipeline/executor.test.d.ts +4 -0
  317. package/dist/src/pipeline/executor.test.js +182 -0
  318. package/dist/src/pipeline/index.d.ts +5 -0
  319. package/dist/src/pipeline/index.js +5 -0
  320. package/dist/src/pipeline/registry.d.ts +28 -0
  321. package/dist/src/pipeline/registry.js +55 -0
  322. package/dist/src/pipeline/steps/browser.d.ts +13 -0
  323. package/dist/src/pipeline/steps/browser.js +86 -0
  324. package/dist/src/pipeline/steps/download.d.ts +18 -0
  325. package/dist/src/pipeline/steps/download.js +252 -0
  326. package/dist/src/pipeline/steps/download.test.d.ts +1 -0
  327. package/dist/src/pipeline/steps/download.test.js +102 -0
  328. package/dist/src/pipeline/steps/fetch.d.ts +5 -0
  329. package/dist/src/pipeline/steps/fetch.js +122 -0
  330. package/dist/src/pipeline/steps/fetch.test.d.ts +1 -0
  331. package/dist/src/pipeline/steps/fetch.test.js +117 -0
  332. package/dist/src/pipeline/steps/intercept.d.ts +5 -0
  333. package/dist/src/pipeline/steps/intercept.js +50 -0
  334. package/dist/src/pipeline/steps/tap.d.ts +12 -0
  335. package/dist/src/pipeline/steps/tap.js +93 -0
  336. package/dist/src/pipeline/steps/transform.d.ts +9 -0
  337. package/dist/src/pipeline/steps/transform.js +70 -0
  338. package/dist/src/pipeline/template.d.ts +17 -0
  339. package/dist/src/pipeline/template.js +344 -0
  340. package/dist/src/pipeline/template.test.d.ts +4 -0
  341. package/dist/src/pipeline/template.test.js +197 -0
  342. package/dist/src/pipeline/transform.test.d.ts +4 -0
  343. package/dist/src/pipeline/transform.test.js +133 -0
  344. package/dist/src/plugin-manifest.d.ts +70 -0
  345. package/dist/src/plugin-manifest.js +160 -0
  346. package/dist/src/plugin-manifest.test.d.ts +4 -0
  347. package/dist/src/plugin-manifest.test.js +179 -0
  348. package/dist/src/plugin-scaffold.d.ts +28 -0
  349. package/dist/src/plugin-scaffold.js +143 -0
  350. package/dist/src/plugin-scaffold.test.d.ts +4 -0
  351. package/dist/src/plugin-scaffold.test.js +83 -0
  352. package/dist/src/plugin.d.ts +146 -0
  353. package/dist/src/plugin.js +1303 -0
  354. package/dist/src/plugin.test.d.ts +4 -0
  355. package/dist/src/plugin.test.js +1363 -0
  356. package/dist/src/registry-api.d.ts +13 -0
  357. package/dist/src/registry-api.js +10 -0
  358. package/dist/src/registry.d.ts +123 -0
  359. package/dist/src/registry.js +125 -0
  360. package/dist/src/registry.test.d.ts +4 -0
  361. package/dist/src/registry.test.js +209 -0
  362. package/dist/src/runtime-copy.test.d.ts +1 -0
  363. package/dist/src/runtime-copy.test.js +29 -0
  364. package/dist/src/runtime-detect.d.ts +31 -0
  365. package/dist/src/runtime-detect.js +47 -0
  366. package/dist/src/runtime-detect.test.d.ts +1 -0
  367. package/dist/src/runtime-detect.test.js +39 -0
  368. package/dist/src/runtime-identity.test.d.ts +1 -0
  369. package/dist/src/runtime-identity.test.js +18 -0
  370. package/dist/src/runtime.d.ts +47 -0
  371. package/dist/src/runtime.js +54 -0
  372. package/dist/src/scripts/framework.d.ts +7 -0
  373. package/dist/src/scripts/framework.js +25 -0
  374. package/dist/src/scripts/interact.d.ts +4 -0
  375. package/dist/src/scripts/interact.js +20 -0
  376. package/dist/src/scripts/store.d.ts +13 -0
  377. package/dist/src/scripts/store.js +48 -0
  378. package/dist/src/serialization.d.ts +42 -0
  379. package/dist/src/serialization.js +103 -0
  380. package/dist/src/serialization.test.d.ts +1 -0
  381. package/dist/src/serialization.test.js +78 -0
  382. package/dist/src/skills.d.ts +14 -0
  383. package/dist/src/skills.js +127 -0
  384. package/dist/src/skills.test.d.ts +1 -0
  385. package/dist/src/skills.test.js +71 -0
  386. package/dist/src/snapshotFormatter.d.ts +11 -0
  387. package/dist/src/snapshotFormatter.js +338 -0
  388. package/dist/src/snapshotFormatter.test.d.ts +7 -0
  389. package/dist/src/snapshotFormatter.test.js +521 -0
  390. package/dist/src/tui.d.ts +28 -0
  391. package/dist/src/tui.js +179 -0
  392. package/dist/src/tui.test.d.ts +1 -0
  393. package/dist/src/tui.test.js +19 -0
  394. package/dist/src/types.d.ts +236 -0
  395. package/dist/src/types.js +7 -0
  396. package/dist/src/update-check.d.ts +66 -0
  397. package/dist/src/update-check.js +206 -0
  398. package/dist/src/update-check.test.d.ts +1 -0
  399. package/dist/src/update-check.test.js +117 -0
  400. package/dist/src/utils.d.ts +57 -0
  401. package/dist/src/utils.js +152 -0
  402. package/dist/src/utils.test.d.ts +1 -0
  403. package/dist/src/utils.test.js +155 -0
  404. package/dist/src/validate.d.ts +21 -0
  405. package/dist/src/validate.js +130 -0
  406. package/dist/src/validate.test.d.ts +9 -0
  407. package/dist/src/validate.test.js +90 -0
  408. package/dist/src/verify.d.ts +26 -0
  409. package/dist/src/verify.js +67 -0
  410. package/dist/src/version.d.ts +4 -0
  411. package/dist/src/version.js +22 -0
  412. package/dist/src/weixin-download.test.d.ts +1 -0
  413. package/dist/src/weixin-download.test.js +45 -0
  414. package/package.json +4 -3
  415. package/scripts/check-package-bin.mjs +95 -0
@@ -0,0 +1,570 @@
1
+ /**
2
+ * Unified target resolver for browser actions.
3
+ *
4
+ * Resolution pipeline:
5
+ *
6
+ * 1. Input classification: all-digit → numeric ref path, otherwise → CSS path.
7
+ * The CSS path passes the raw string to `querySelectorAll` and lets the
8
+ * browser parser decide what's valid. No frontend regex whitelist — the
9
+ * goal is that any selector accepted by `browser find --css` is accepted
10
+ * by the same selector on `get/click/type/select`.
11
+ * 2. Ref path: cascading match levels (see below), using data-webcmd-ref
12
+ * plus the fingerprint map populated by snapshot + find.
13
+ * 3. CSS path: querySelectorAll + match-count policy (see ResolveOptions)
14
+ * 4. Structured errors:
15
+ * - numeric: not_found / stale_ref
16
+ * - CSS: invalid_selector / selector_not_found / selector_ambiguous
17
+ * / selector_nth_out_of_range
18
+ *
19
+ * All JS is generated as strings for page.evaluate() — runs in the browser.
20
+ *
21
+ * ── Cascading stale-ref (browser-use style) ──────────────────────────
22
+ * Strict equality on the fingerprint rejected too many live pages — SPA
23
+ * re-renders swap text / role while keeping id + testId. The resolver
24
+ * now walks three tiers before giving up:
25
+ *
26
+ * 1. EXACT — tag + strong id (id or testId) agree, ≤1 soft mismatch
27
+ * 2. STABLE — tag + strong id agree, soft signals drifted (aria-label,
28
+ * role, text) — agent gets a warning but the action
29
+ * proceeds so dynamic pages don't stall
30
+ * 3. REIDENTIFIED — original ref either missing from the DOM or fully
31
+ * mismatched, but the fingerprint uniquely identifies
32
+ * a single other live element via id / testId /
33
+ * aria-label. Re-tag that element with the old ref and
34
+ * surface match_level so the caller knows we swapped.
35
+ *
36
+ * Only when all three fail do we emit `stale_ref`. Every success envelope
37
+ * carries `match_level` so downstream CLIs can surface the weakest tier
38
+ * a caller actually traversed.
39
+ */
40
+ /**
41
+ * Generate JS that resolves a target to a single DOM element.
42
+ *
43
+ * Returns a JS expression that evaluates to:
44
+ * { ok: true, matches_n, match_level } — success (el stored in `__resolved`)
45
+ * { ok: false, code, message, hint, candidates, matches_n? } — structured error
46
+ *
47
+ * `match_level` is always set on success:
48
+ * - CSS path → 'exact'
49
+ * - numeric ref path → whichever tier matched ('exact' / 'stable' / 'reidentified')
50
+ *
51
+ * The resolved element is stored in `window.__resolved` for downstream helpers.
52
+ */
53
+ export function resolveTargetJs(ref, opts = {}) {
54
+ const safeRef = JSON.stringify(ref);
55
+ const nthJs = opts.nth !== undefined ? String(opts.nth | 0) : 'null';
56
+ const firstOnMulti = opts.firstOnMulti === true ? 'true' : 'false';
57
+ return `
58
+ (() => {
59
+ const ref = ${safeRef};
60
+ const nth = ${nthJs};
61
+ const firstOnMulti = ${firstOnMulti};
62
+ const identity = window.__webcmd_ref_identity || {};
63
+
64
+ // ── Classify input ──
65
+ // Numeric = snapshot ref. Everything else is handed to querySelectorAll
66
+ // and whatever the browser parser accepts is a valid selector. No regex
67
+ // shortlist up front: \`find --css\` and \`get/click/type/select\` must agree
68
+ // on the same selector surface (see contract note at the top of this file).
69
+ const isNumeric = /^\\d+$/.test(ref);
70
+
71
+ if (isNumeric) {
72
+ // ── Ref path (cascading match levels) ──
73
+
74
+ // Shared helper: compute a fingerprint off a live element, same shape
75
+ // snapshot + find populate into \`__webcmd_ref_identity\`. Kept inline
76
+ // (not imported) because this source string is compiled standalone.
77
+ function fingerprintOf(node) {
78
+ return {
79
+ tag: node.tagName.toLowerCase(),
80
+ role: node.getAttribute('role') || '',
81
+ text: (node.textContent || '').trim().slice(0, 30),
82
+ ariaLabel: node.getAttribute('aria-label') || '',
83
+ id: node.id || '',
84
+ testId: node.getAttribute('data-testid') || node.getAttribute('data-test') || '',
85
+ };
86
+ }
87
+
88
+ // Classify how strongly a live element matches a stored fingerprint.
89
+ // Returns one of 'exact' | 'stable' | 'mismatch'.
90
+ //
91
+ // 'exact' — tag + every non-empty stored field agrees (±text prefix).
92
+ // 'stable' — tag agrees AND at least one strong id (id or testId) still
93
+ // matches; soft signals (aria-label, role, text) may have
94
+ // drifted. This covers SPA re-render / i18n label swaps.
95
+ // 'mismatch' otherwise.
96
+ function classifyMatch(fp, liveFp) {
97
+ if (fp.tag !== liveFp.tag) return 'mismatch';
98
+
99
+ const idMatch = !fp.id || fp.id === liveFp.id;
100
+ const testIdMatch = !fp.testId || fp.testId === liveFp.testId;
101
+ const roleMatch = !fp.role || fp.role === liveFp.role;
102
+ const ariaMatch = !fp.ariaLabel || fp.ariaLabel === liveFp.ariaLabel;
103
+ const textMatch = !fp.text || (
104
+ !!liveFp.text && (liveFp.text.startsWith(fp.text) || fp.text.startsWith(liveFp.text))
105
+ );
106
+
107
+ if (idMatch && testIdMatch && roleMatch && ariaMatch && textMatch) return 'exact';
108
+
109
+ // Strong id decides: if id + testId still agree and we had at least one
110
+ // of them, accept as stable regardless of soft-signal drift.
111
+ const hadStrongId = !!fp.id || !!fp.testId;
112
+ if (hadStrongId && idMatch && testIdMatch) return 'stable';
113
+
114
+ return 'mismatch';
115
+ }
116
+
117
+ // Try to recover a stale ref by searching the page for a live element
118
+ // whose fingerprint still matches. Uniqueness is required — if two
119
+ // candidates match equally well, we refuse rather than silently pick
120
+ // the wrong one. Covers ref annotations lost to a re-mount.
121
+ function reidentify(fp) {
122
+ if (!fp) return null;
123
+ const candidates = [];
124
+ function tryAdd(el) {
125
+ if (el && el.nodeType === 1 && classifyMatch(fp, fingerprintOf(el)) !== 'mismatch') {
126
+ if (candidates.indexOf(el) === -1) candidates.push(el);
127
+ }
128
+ }
129
+ // Prefer strong-id lookups. If id / testId is present and yields a
130
+ // unique element, that's our hit.
131
+ try {
132
+ if (fp.id) {
133
+ const byId = document.getElementById(fp.id);
134
+ if (byId) tryAdd(byId);
135
+ }
136
+ if (fp.testId) {
137
+ const byTestIdA = document.querySelectorAll('[data-testid="' + fp.testId.replace(/"/g, '\\\\"') + '"]');
138
+ for (let i = 0; i < byTestIdA.length; i++) tryAdd(byTestIdA[i]);
139
+ const byTestIdB = document.querySelectorAll('[data-test="' + fp.testId.replace(/"/g, '\\\\"') + '"]');
140
+ for (let i = 0; i < byTestIdB.length; i++) tryAdd(byTestIdB[i]);
141
+ }
142
+ // aria-label is only a useful shortlist when nothing stronger is set
143
+ if (candidates.length === 0 && fp.ariaLabel) {
144
+ const byAria = document.querySelectorAll('[aria-label="' + fp.ariaLabel.replace(/"/g, '\\\\"') + '"]');
145
+ for (let i = 0; i < byAria.length; i++) tryAdd(byAria[i]);
146
+ }
147
+ } catch (_) { /* bad selectors from weird fp values — skip */ }
148
+ return candidates.length === 1 ? candidates[0] : null;
149
+ }
150
+
151
+ const fp = identity[ref];
152
+ let el = document.querySelector('[data-webcmd-ref="' + ref + '"]');
153
+ if (!el) el = document.querySelector('[data-ref="' + ref + '"]');
154
+
155
+ // If the ref tag is gone from the DOM, last-chance reidentify.
156
+ if (!el) {
157
+ const recovered = reidentify(fp);
158
+ if (recovered) {
159
+ try {
160
+ recovered.setAttribute('data-webcmd-ref', ref);
161
+ identity[ref] = fingerprintOf(recovered);
162
+ } catch (_) {}
163
+ window.__resolved = recovered;
164
+ return { ok: true, matches_n: 1, match_level: 'reidentified' };
165
+ }
166
+ return {
167
+ ok: false,
168
+ code: 'not_found',
169
+ message: 'ref=' + ref + ' not found in DOM',
170
+ hint: 'The element may have been removed. Re-run \`webcmd browser state\` to get a fresh snapshot.',
171
+ };
172
+ }
173
+
174
+ // No stored fingerprint (older page / unknown ref) — accept as exact.
175
+ if (!fp) {
176
+ window.__resolved = el;
177
+ return { ok: true, matches_n: 1, match_level: 'exact' };
178
+ }
179
+
180
+ const liveFp = fingerprintOf(el);
181
+ const level = classifyMatch(fp, liveFp);
182
+
183
+ if (level === 'exact' || level === 'stable') {
184
+ window.__resolved = el;
185
+ return { ok: true, matches_n: 1, match_level: level };
186
+ }
187
+
188
+ // Tag / strong-id mismatch — try to find the real element elsewhere
189
+ // before giving up. Covers e.g. a modal re-mount that discarded the
190
+ // data-webcmd-ref attribute on the surviving node.
191
+ const recovered = reidentify(fp);
192
+ if (recovered && recovered !== el) {
193
+ try {
194
+ el.removeAttribute('data-webcmd-ref');
195
+ recovered.setAttribute('data-webcmd-ref', ref);
196
+ identity[ref] = fingerprintOf(recovered);
197
+ } catch (_) {}
198
+ window.__resolved = recovered;
199
+ return { ok: true, matches_n: 1, match_level: 'reidentified' };
200
+ }
201
+
202
+ return {
203
+ ok: false,
204
+ code: 'stale_ref',
205
+ message: 'ref=' + ref + ' was <' + fp.tag + '>' + (fp.text ? '"' + fp.text + '"' : '')
206
+ + ' but now points to <' + liveFp.tag + '>' + (liveFp.text ? '"' + liveFp.text.slice(0, 30) + '"' : ''),
207
+ hint: 'The page has changed since the last snapshot. Re-run \`webcmd browser state\` to refresh.',
208
+ };
209
+ }
210
+
211
+ // ── CSS selector path (any non-numeric input) ──
212
+ {
213
+ let matches;
214
+ try {
215
+ matches = document.querySelectorAll(ref);
216
+ } catch (e) {
217
+ return {
218
+ ok: false,
219
+ code: 'invalid_selector',
220
+ message: 'Invalid CSS selector: ' + ref + ' (' + ((e && e.message) || String(e)) + ')',
221
+ hint: 'Check the selector syntax. Use ref numbers from snapshot for reliable targeting.',
222
+ };
223
+ }
224
+
225
+ if (matches.length === 0) {
226
+ return {
227
+ ok: false,
228
+ code: 'selector_not_found',
229
+ message: 'CSS selector "' + ref + '" matched 0 elements',
230
+ hint: 'The element may not exist or may be hidden. Re-run \`webcmd browser state\` to check, or use \`webcmd browser find --css\` to explore candidates.',
231
+ matches_n: 0,
232
+ };
233
+ }
234
+
235
+ if (nth !== null) {
236
+ if (nth < 0 || nth >= matches.length) {
237
+ return {
238
+ ok: false,
239
+ code: 'selector_nth_out_of_range',
240
+ message: 'CSS selector "' + ref + '" matched ' + matches.length + ' elements, but --nth=' + nth + ' is out of range',
241
+ hint: 'Use --nth between 0 and ' + (matches.length - 1) + ', or omit --nth to target the first match (read ops) or require explicit disambiguation (write ops).',
242
+ matches_n: matches.length,
243
+ };
244
+ }
245
+ window.__resolved = matches[nth];
246
+ return { ok: true, matches_n: matches.length, match_level: 'exact' };
247
+ }
248
+
249
+ if (matches.length > 1 && !firstOnMulti) {
250
+ const candidates = [];
251
+ const limit = Math.min(matches.length, 5);
252
+ for (let i = 0; i < limit; i++) {
253
+ const m = matches[i];
254
+ const tag = m.tagName.toLowerCase();
255
+ const text = (m.textContent || '').trim().slice(0, 40);
256
+ const id = m.id ? '#' + m.id : '';
257
+ candidates.push('<' + tag + id + '>' + (text ? ' "' + text + '"' : ''));
258
+ }
259
+ return {
260
+ ok: false,
261
+ code: 'selector_ambiguous',
262
+ message: 'CSS selector "' + ref + '" matched ' + matches.length + ' elements',
263
+ hint: 'Pass --nth <n> (0-based) to pick one, or use a more specific selector. Use \`webcmd browser find --css\` to list all candidates.',
264
+ candidates: candidates,
265
+ matches_n: matches.length,
266
+ };
267
+ }
268
+
269
+ // Single match, OR multi-match with firstOnMulti (read path)
270
+ window.__resolved = matches[0];
271
+ return { ok: true, matches_n: matches.length, match_level: 'exact' };
272
+ }
273
+ })()
274
+ `;
275
+ }
276
+ /**
277
+ * Generate JS that scrolls + measures `__resolved` without clicking.
278
+ *
279
+ * Generic click prefers CDP `Input.dispatchMouseEvent`, which fires the full
280
+ * pointer/mouse chain that Radix/MUI/shadcn dropdowns rely on. Keep measurement
281
+ * separate so the CDP-primary path does not call DOM `el.click()` first.
282
+ */
283
+ export function boundingRectResolvedJs(opts = {}) {
284
+ const shouldScroll = opts.skipScroll ? 'false' : 'true';
285
+ return `
286
+ (() => {
287
+ const el = window.__resolved;
288
+ if (!el) throw new Error('No resolved element');
289
+ if (${shouldScroll}) el.scrollIntoView({ behavior: 'instant', block: 'center' });
290
+ const rect = el.getBoundingClientRect();
291
+ const w = Math.round(rect.width);
292
+ const h = Math.round(rect.height);
293
+ const x = Math.round(rect.left + rect.width / 2);
294
+ const y = Math.round(rect.top + rect.height / 2);
295
+ const visible = w > 0 && h > 0;
296
+ return { x, y, w, h, visible };
297
+ })()
298
+ `;
299
+ }
300
+ /**
301
+ * Generate JS for click that uses the unified resolver.
302
+ * Assumes resolveTargetJs has been called and __resolved is set.
303
+ *
304
+ * This is the JS fallback path. BasePage.click uses boundingRectResolvedJs for
305
+ * the CDP-primary path and only reaches this when native click is unavailable
306
+ * or the target has no usable rect.
307
+ */
308
+ export function clickResolvedJs(opts = {}) {
309
+ const shouldScroll = opts.skipScroll ? 'false' : 'true';
310
+ return `
311
+ (() => {
312
+ const el = window.__resolved;
313
+ if (!el) throw new Error('No resolved element');
314
+ if (${shouldScroll}) el.scrollIntoView({ behavior: 'instant', block: 'center' });
315
+ const rect = el.getBoundingClientRect();
316
+ const x = Math.round(rect.left + rect.width / 2);
317
+ const y = Math.round(rect.top + rect.height / 2);
318
+ try {
319
+ el.click();
320
+ return { status: 'clicked', x, y, w: Math.round(rect.width), h: Math.round(rect.height) };
321
+ } catch (e) {
322
+ return { status: 'js_failed', x, y, w: Math.round(rect.width), h: Math.round(rect.height), error: e.message };
323
+ }
324
+ })()
325
+ `;
326
+ }
327
+ /**
328
+ * Generate JS for type that uses the unified resolver.
329
+ */
330
+ export function typeResolvedJs(text) {
331
+ const safeText = JSON.stringify(text);
332
+ return `
333
+ (() => {
334
+ const el = window.__resolved;
335
+ if (!el) throw new Error('No resolved element');
336
+ el.focus();
337
+ if (el.isContentEditable) {
338
+ const sel = window.getSelection();
339
+ const range = document.createRange();
340
+ range.selectNodeContents(el);
341
+ sel.removeAllRanges();
342
+ sel.addRange(range);
343
+ document.execCommand('delete', false);
344
+ document.execCommand('insertText', false, ${safeText});
345
+ el.dispatchEvent(new Event('input', { bubbles: true }));
346
+ } else {
347
+ const proto = el instanceof HTMLTextAreaElement
348
+ ? HTMLTextAreaElement.prototype
349
+ : HTMLInputElement.prototype;
350
+ const nativeSetter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
351
+ if (nativeSetter) {
352
+ nativeSetter.call(el, ${safeText});
353
+ } else {
354
+ el.value = ${safeText};
355
+ }
356
+ el.dispatchEvent(new Event('input', { bubbles: true }));
357
+ el.dispatchEvent(new Event('change', { bubbles: true }));
358
+ }
359
+ return 'typed';
360
+ })()
361
+ `;
362
+ }
363
+ /**
364
+ * Prepare the resolved element for native CDP Input.insertText.
365
+ *
366
+ * This preserves `browser type`'s existing "replace current text" semantics:
367
+ * focus the editable target, select its current contents, then let CDP insert
368
+ * real browser text input so rich editors can update their internal state.
369
+ */
370
+ export function prepareNativeTypeResolvedJs(opts = {}) {
371
+ const shouldScroll = opts.skipScroll ? 'false' : 'true';
372
+ const shouldFocus = opts.skipFocus ? 'false' : 'true';
373
+ return `
374
+ (() => {
375
+ const original = window.__resolved;
376
+ if (!original) throw new Error('No resolved element');
377
+
378
+ function nearestContentEditableHost(el) {
379
+ let current = el;
380
+ while (current && current.nodeType === 1) {
381
+ if (current.hasAttribute && current.hasAttribute('contenteditable')) return current;
382
+ current = current.parentElement;
383
+ }
384
+ return el.isContentEditable ? el : null;
385
+ }
386
+
387
+ const editableHost = original.isContentEditable ? nearestContentEditableHost(original) : null;
388
+ const inputTypes = new Set(['', 'text', 'search', 'url', 'tel', 'email', 'password']);
389
+ const isInput = original instanceof HTMLInputElement;
390
+ const isTextarea = original instanceof HTMLTextAreaElement;
391
+ const isTextControl = isTextarea || (isInput && inputTypes.has((original.getAttribute('type') || original.type || '').toLowerCase()));
392
+ const el = editableHost || (isTextControl ? original : null);
393
+
394
+ if (!el) {
395
+ return {
396
+ ok: false,
397
+ reason: 'not_editable',
398
+ tag: original.tagName ? original.tagName.toLowerCase() : '',
399
+ };
400
+ }
401
+
402
+ window.__resolved = el;
403
+ if (${shouldScroll}) el.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
404
+ if (${shouldFocus}) {
405
+ try {
406
+ el.focus({ preventScroll: true });
407
+ } catch (_) {
408
+ el.focus();
409
+ }
410
+ }
411
+
412
+ if (editableHost) {
413
+ const sel = window.getSelection();
414
+ if (!sel) return { ok: false, reason: 'selection_unavailable', mode: 'contenteditable' };
415
+ const range = document.createRange();
416
+ range.selectNodeContents(el);
417
+ sel.removeAllRanges();
418
+ sel.addRange(range);
419
+ return { ok: true, mode: 'contenteditable' };
420
+ }
421
+
422
+ let selected = false;
423
+ try {
424
+ if (typeof el.setSelectionRange === 'function') {
425
+ el.setSelectionRange(0, String(el.value || '').length);
426
+ selected = true;
427
+ }
428
+ } catch (_) {}
429
+ try {
430
+ if (!selected && typeof el.select === 'function') {
431
+ el.select();
432
+ selected = true;
433
+ }
434
+ } catch (_) {}
435
+
436
+ return selected
437
+ ? { ok: true, mode: isTextarea ? 'textarea' : 'input' }
438
+ : { ok: false, reason: 'selection_unavailable', mode: isTextarea ? 'textarea' : 'input' };
439
+ })()
440
+ `;
441
+ }
442
+ /**
443
+ * Verify the exact value/text currently held by the resolved editable target.
444
+ * Assumes resolveTargetJs and prepareNativeTypeResolvedJs have already set
445
+ * `window.__resolved` to the normalized editable host.
446
+ */
447
+ export function verifyFilledResolvedJs(expected) {
448
+ const safeText = JSON.stringify(expected);
449
+ return `
450
+ (() => {
451
+ const el = window.__resolved;
452
+ if (!el) return { ok: false, reason: 'no_resolved_element' };
453
+
454
+ const tag = el.tagName ? el.tagName.toLowerCase() : '';
455
+ const isInput = el instanceof HTMLInputElement;
456
+ const isTextarea = el instanceof HTMLTextAreaElement;
457
+ const mode = el.isContentEditable
458
+ ? 'contenteditable'
459
+ : isTextarea
460
+ ? 'textarea'
461
+ : isInput
462
+ ? 'input'
463
+ : '';
464
+
465
+ if (!mode) {
466
+ return {
467
+ ok: false,
468
+ reason: 'not_editable',
469
+ tag,
470
+ role: el.getAttribute ? (el.getAttribute('role') || '') : '',
471
+ };
472
+ }
473
+
474
+ const actual = mode === 'contenteditable' ? (el.innerText || '') : String(el.value || '');
475
+ return {
476
+ ok: actual === ${safeText},
477
+ actual,
478
+ expected: ${safeText},
479
+ length: actual.length,
480
+ mode,
481
+ };
482
+ })()
483
+ `;
484
+ }
485
+ /**
486
+ * Generate JS for scrollTo that uses the unified resolver.
487
+ * Assumes resolveTargetJs has been called and __resolved is set.
488
+ */
489
+ export function scrollResolvedJs(opts = {}) {
490
+ const shouldScroll = opts.skipScroll ? 'false' : 'true';
491
+ return `
492
+ (() => {
493
+ const el = window.__resolved;
494
+ if (!el) throw new Error('No resolved element');
495
+ if (${shouldScroll}) el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
496
+ return { scrolled: true, tag: el.tagName.toLowerCase(), text: (el.textContent || '').trim().slice(0, 80) };
497
+ })()
498
+ `;
499
+ }
500
+ /**
501
+ * Generate JS to get text content of resolved element.
502
+ */
503
+ export function getTextResolvedJs() {
504
+ return `
505
+ (() => {
506
+ const el = window.__resolved;
507
+ if (!el) throw new Error('No resolved element');
508
+ return el.textContent?.trim() ?? null;
509
+ })()
510
+ `;
511
+ }
512
+ /**
513
+ * Generate JS to get value of resolved input/textarea element.
514
+ */
515
+ export function getValueResolvedJs() {
516
+ return `
517
+ (() => {
518
+ const el = window.__resolved;
519
+ if (!el) throw new Error('No resolved element');
520
+ return el.value ?? null;
521
+ })()
522
+ `;
523
+ }
524
+ /**
525
+ * Generate JS to get all attributes of resolved element.
526
+ */
527
+ export function getAttributesResolvedJs() {
528
+ return `
529
+ (() => {
530
+ const el = window.__resolved;
531
+ if (!el) throw new Error('No resolved element');
532
+ return JSON.stringify(Object.fromEntries([...el.attributes].map(a => [a.name, a.value])));
533
+ })()
534
+ `;
535
+ }
536
+ /**
537
+ * Generate JS to select an option on a resolved <select> element.
538
+ */
539
+ export function selectResolvedJs(option) {
540
+ const safeOption = JSON.stringify(option);
541
+ return `
542
+ (() => {
543
+ const el = window.__resolved;
544
+ if (!el) throw new Error('No resolved element');
545
+ if (el.tagName !== 'SELECT') return { error: 'Not a <select>' };
546
+ const match = Array.from(el.options).find(o => o.text.trim() === ${safeOption} || o.value === ${safeOption});
547
+ if (!match) return { error: 'Option not found', available: Array.from(el.options).map(o => o.text.trim()) };
548
+ const setter = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, 'value')?.set;
549
+ if (setter) setter.call(el, match.value); else el.value = match.value;
550
+ el.dispatchEvent(new Event('input', { bubbles: true }));
551
+ el.dispatchEvent(new Event('change', { bubbles: true }));
552
+ return { selected: match.text };
553
+ })()
554
+ `;
555
+ }
556
+ /**
557
+ * Generate JS to check if resolved element is an autocomplete/combobox field.
558
+ */
559
+ export function isAutocompleteResolvedJs() {
560
+ return `
561
+ (() => {
562
+ const el = window.__resolved;
563
+ if (!el) return false;
564
+ const role = el.getAttribute('role');
565
+ const ac = el.getAttribute('aria-autocomplete');
566
+ const list = el.getAttribute('list');
567
+ return role === 'combobox' || ac === 'list' || ac === 'both' || !!list;
568
+ })()
569
+ `;
570
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,118 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { resolveTargetJs } from './target-resolver.js';
3
+ /**
4
+ * Tests for the target resolver JS generator.
5
+ *
6
+ * Since resolveTargetJs() produces JS strings for browser evaluate(),
7
+ * we test the generated JS by running it in a simulated DOM-like context
8
+ * and verifying the structure of the output.
9
+ */
10
+ describe('resolveTargetJs', () => {
11
+ it('generates JS that returns structured resolution for numeric ref', () => {
12
+ const js = resolveTargetJs('12');
13
+ expect(js).toContain('data-webcmd-ref');
14
+ expect(js).toContain('__webcmd_ref_identity');
15
+ expect(js).toContain('"12"');
16
+ });
17
+ it('generates JS that handles CSS selector input', () => {
18
+ const js = resolveTargetJs('#submit-btn');
19
+ expect(js).toContain('querySelectorAll');
20
+ expect(js).toContain('"#submit-btn"');
21
+ });
22
+ it('generates JS with stale_ref detection for numeric refs', () => {
23
+ const js = resolveTargetJs('5');
24
+ expect(js).toContain('stale_ref');
25
+ expect(js).toContain('__webcmd_ref_identity');
26
+ });
27
+ it('generates JS with ambiguity detection for CSS selectors', () => {
28
+ const js = resolveTargetJs('.btn');
29
+ expect(js).toContain('selector_ambiguous');
30
+ expect(js).toContain('candidates');
31
+ });
32
+ it('generates JS that propagates --nth option into the CSS branch', () => {
33
+ const js = resolveTargetJs('.btn', { nth: 2 });
34
+ expect(js).toContain('selector_nth_out_of_range');
35
+ // opt.nth=2 should be inlined so the runtime picks matches[2]
36
+ expect(js).toMatch(/const nth = 2;?/);
37
+ });
38
+ it('generates JS that enables firstOnMulti for read commands', () => {
39
+ const js = resolveTargetJs('.btn', { firstOnMulti: true });
40
+ expect(js).toContain('firstOnMulti = true');
41
+ });
42
+ it('generates JS with invalid_selector branch for CSS syntax errors', () => {
43
+ const js = resolveTargetJs('.btn');
44
+ expect(js).toContain('invalid_selector');
45
+ });
46
+ it('generates JS with selector_not_found branch for 0 matches', () => {
47
+ const js = resolveTargetJs('#does-not-exist');
48
+ expect(js).toContain('selector_not_found');
49
+ });
50
+ it('hands every non-numeric input to querySelectorAll (no regex shortlist)', () => {
51
+ // Inputs that the old isCssLike regex rejected — must all flow into the
52
+ // CSS branch so `find --css` and `get/click/type/select` accept the same surface.
53
+ for (const sel of [':root', '*', ':has(.foo)', '::shadow-root', '???']) {
54
+ const js = resolveTargetJs(sel);
55
+ expect(js).toContain('querySelectorAll');
56
+ // invalid selectors still route through invalid_selector at runtime,
57
+ // never through a frontend "Cannot parse target" rejection.
58
+ expect(js).not.toContain('Cannot parse target');
59
+ }
60
+ });
61
+ it('escapes ref value safely', () => {
62
+ const js = resolveTargetJs('"; alert(1); "');
63
+ // JSON.stringify should handle escaping
64
+ expect(js).not.toContain('alert(1); "');
65
+ expect(js).toContain('\\"');
66
+ });
67
+ it('tags every success envelope with match_level so agents can tell tiers apart', () => {
68
+ const numericJs = resolveTargetJs('7');
69
+ const cssJs = resolveTargetJs('.btn');
70
+ // Exact / reidentified emit the literal directly; stable flows through the
71
+ // classifier's `level` variable. All three strings must appear in the JS.
72
+ expect(numericJs).toContain("match_level: 'exact'");
73
+ expect(numericJs).toContain("match_level: 'reidentified'");
74
+ expect(numericJs).toContain("return 'stable'");
75
+ // Stable + exact share the same emit site (match_level: level) — make sure
76
+ // we didn't hardcode one of them and drop the other.
77
+ expect(numericJs).toContain('match_level: level');
78
+ // CSS path is always exact (selector ran successfully).
79
+ expect(cssJs).toContain("match_level: 'exact'");
80
+ });
81
+ it('cascading ref path — classifier + reidentifier are both wired in', () => {
82
+ const js = resolveTargetJs('3');
83
+ // Classifier distinguishes the three tiers
84
+ expect(js).toContain('function classifyMatch');
85
+ expect(js).toContain("return 'exact'");
86
+ expect(js).toContain("return 'stable'");
87
+ expect(js).toContain("return 'mismatch'");
88
+ // Strong id is the only thing that can rescue a drifted fingerprint
89
+ expect(js).toContain('hadStrongId');
90
+ // Reidentify searches live DOM with the same fingerprint shape the
91
+ // snapshot / find writers emit — id / testId / aria-label only.
92
+ expect(js).toContain('function reidentify');
93
+ expect(js).toContain('getElementById');
94
+ expect(js).toContain('[data-testid="');
95
+ expect(js).toContain('[aria-label="');
96
+ // Unique match required — never silently picks one of many candidates.
97
+ expect(js).toContain('candidates.length === 1');
98
+ // Recovered element is re-tagged + identity map refreshed so subsequent
99
+ // resolves land on 'exact' instead of re-walking the cascade.
100
+ expect(js).toContain("setAttribute('data-webcmd-ref', ref)");
101
+ expect(js).toContain('identity[ref] = fingerprintOf(recovered)');
102
+ });
103
+ it('reidentify runs both when data-webcmd-ref is missing AND when fingerprint is mismatched', () => {
104
+ const js = resolveTargetJs('9');
105
+ // Two call sites: one in the !el branch, one after classifyMatch returns mismatch.
106
+ const count = js.split('reidentify(fp)').length - 1;
107
+ expect(count).toBeGreaterThanOrEqual(2);
108
+ });
109
+ it('falls through to stale_ref only after reidentify exhausts', () => {
110
+ const js = resolveTargetJs('4');
111
+ // The stale_ref emit must sit *below* a reidentify attempt so the cascade
112
+ // is what produces the error — not the original strict check.
113
+ const reidentifyIdx = js.indexOf('const recovered = reidentify(fp);');
114
+ const staleIdx = js.indexOf("code: 'stale_ref'");
115
+ expect(reidentifyIdx).toBeGreaterThan(-1);
116
+ expect(staleIdx).toBeGreaterThan(reidentifyIdx);
117
+ });
118
+ });