@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,219 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { deriveFixture, expandFixtureArgs, parseSeedArgs, validateRows, validateRowShape } from './verify-fixture.js';
3
+ describe('validateRows', () => {
4
+ it('passes when rows meet all expectations', () => {
5
+ const fixture = {
6
+ expect: {
7
+ rowCount: { min: 1, max: 3 },
8
+ columns: ['id', 'title', 'url'],
9
+ types: { id: 'number', title: 'string', url: 'string' },
10
+ patterns: { url: '^https://' },
11
+ notEmpty: ['title', 'url'],
12
+ },
13
+ };
14
+ const rows = [
15
+ { id: 1, title: 'a', url: 'https://x.com/a' },
16
+ { id: 2, title: 'b', url: 'https://x.com/b' },
17
+ ];
18
+ expect(validateRows(rows, fixture)).toEqual([]);
19
+ });
20
+ it('reports rowCount below min', () => {
21
+ const failures = validateRows([], { expect: { rowCount: { min: 1 } } });
22
+ expect(failures).toHaveLength(1);
23
+ expect(failures[0]).toMatchObject({ rule: 'rowCount' });
24
+ expect(failures[0].detail).toContain('at least 1');
25
+ });
26
+ it('reports rowCount above max', () => {
27
+ const failures = validateRows([{}, {}, {}, {}], { expect: { rowCount: { max: 3 } } });
28
+ expect(failures).toHaveLength(1);
29
+ expect(failures[0].detail).toContain('at most 3');
30
+ });
31
+ it('reports missing columns per row', () => {
32
+ const failures = validateRows([{ a: 1 }, { a: 2, b: 3 }], { expect: { columns: ['a', 'b'] } });
33
+ // row 0 missing 'b', row 1 complete
34
+ expect(failures).toEqual([
35
+ { rule: 'column', detail: 'missing column "b"', rowIndex: 0 },
36
+ ]);
37
+ });
38
+ it('reports type mismatch including null', () => {
39
+ const failures = validateRows([{ a: 'abc' }, { a: null }, { a: 42 }], { expect: { types: { a: 'string' } } });
40
+ // row 0 string ok, row 1 null fail, row 2 number fail
41
+ expect(failures).toHaveLength(2);
42
+ expect(failures[0].rowIndex).toBe(1);
43
+ expect(failures[0].detail).toContain('null');
44
+ expect(failures[1].rowIndex).toBe(2);
45
+ expect(failures[1].detail).toContain('number');
46
+ });
47
+ it('accepts union types like "number|string"', () => {
48
+ const failures = validateRows([{ id: 1 }, { id: 'abc' }], { expect: { types: { id: 'number|string' } } });
49
+ expect(failures).toEqual([]);
50
+ });
51
+ it('accepts "any" as wildcard type', () => {
52
+ const failures = validateRows([{ v: 1 }, { v: 'x' }, { v: null }, { v: [1, 2] }], { expect: { types: { v: 'any' } } });
53
+ expect(failures).toEqual([]);
54
+ });
55
+ it('reports pattern mismatch with row index and truncated value', () => {
56
+ const failures = validateRows([{ url: 'https://ok.com' }, { url: 'not-a-url' }], { expect: { patterns: { url: '^https?://' } } });
57
+ expect(failures).toHaveLength(1);
58
+ expect(failures[0]).toMatchObject({ rule: 'pattern', rowIndex: 1 });
59
+ expect(failures[0].detail).toContain('not-a-url');
60
+ });
61
+ it('skips pattern check for null/undefined values', () => {
62
+ const failures = validateRows([{ url: null }, { url: undefined }], { expect: { patterns: { url: '^x' } } });
63
+ expect(failures).toEqual([]);
64
+ });
65
+ it('reports invalid regex without crashing', () => {
66
+ const failures = validateRows([{ a: 'x' }], { expect: { patterns: { a: '[unclosed' } } });
67
+ expect(failures.some((f) => f.rule === 'pattern' && f.detail.includes('invalid'))).toBe(true);
68
+ });
69
+ it('treats empty/whitespace/null as failing notEmpty', () => {
70
+ const failures = validateRows([{ t: '' }, { t: ' ' }, { t: null }, { t: 'ok' }], { expect: { notEmpty: ['t'] } });
71
+ expect(failures).toHaveLength(3);
72
+ expect(failures.map((f) => f.rowIndex)).toEqual([0, 1, 2]);
73
+ });
74
+ it('no failures when fixture has no expect block', () => {
75
+ expect(validateRows([{ anything: 1 }], {})).toEqual([]);
76
+ });
77
+ it('mustNotContain flags substring bleed in columns', () => {
78
+ const failures = validateRows([
79
+ { description: 'Lead engineer, 5 years exp. address: Shanghai. category: IT' },
80
+ { description: 'Clean text.' },
81
+ ], {
82
+ expect: {
83
+ mustNotContain: { description: ['address:', 'category:'] },
84
+ },
85
+ });
86
+ expect(failures).toHaveLength(2);
87
+ expect(failures.every((f) => f.rule === 'mustNotContain')).toBe(true);
88
+ expect(failures.every((f) => f.rowIndex === 0)).toBe(true);
89
+ });
90
+ it('mustNotContain skips null/undefined values', () => {
91
+ const failures = validateRows([{ description: null }, { description: undefined }], { expect: { mustNotContain: { description: ['x'] } } });
92
+ expect(failures).toEqual([]);
93
+ });
94
+ it('mustBeTruthy catches silent 0 / false / "" fallbacks', () => {
95
+ const failures = validateRows([{ count: 10 }, { count: 0 }, { count: false }, { count: '' }, { count: null }], { expect: { mustBeTruthy: ['count'] } });
96
+ expect(failures).toHaveLength(4);
97
+ expect(failures.every((f) => f.rule === 'mustBeTruthy')).toBe(true);
98
+ expect(failures.map((f) => f.rowIndex)).toEqual([1, 2, 3, 4]);
99
+ });
100
+ });
101
+ describe('validateRowShape', () => {
102
+ it('passes flat rows with a compact key set', () => {
103
+ const failures = validateRowShape([
104
+ { id: '1', title: 'A', author: { name: 'Ada' }, tags: ['ai', 'web'] },
105
+ ]);
106
+ expect(failures).toEqual([]);
107
+ });
108
+ it('reports rows with too many top-level keys', () => {
109
+ const row = Object.fromEntries(Array.from({ length: 13 }, (_, i) => [`k${i}`, i]));
110
+ const failures = validateRowShape([row]);
111
+ expect(failures).toEqual([
112
+ {
113
+ rule: 'shapeKeyCount',
114
+ detail: 'row has 13 top-level keys, expected at most 12',
115
+ rowIndex: 0,
116
+ },
117
+ ]);
118
+ });
119
+ it('reports nesting deeper than one level', () => {
120
+ const failures = validateRowShape([
121
+ { title: 'A', stats: { author: { name: 'Ada' } } },
122
+ ]);
123
+ expect(failures).toEqual([
124
+ {
125
+ rule: 'shapeDepth',
126
+ detail: '"stats" nesting depth is 2, expected at most 1',
127
+ rowIndex: 0,
128
+ },
129
+ ]);
130
+ });
131
+ it('reports nested id-shaped fields even when one-level nesting is otherwise allowed', () => {
132
+ const failures = validateRowShape([
133
+ { title: 'A', author: { user_id: 'u1', name: 'Ada' } },
134
+ ]);
135
+ expect(failures).toEqual([
136
+ {
137
+ rule: 'shapeNestedId',
138
+ detail: 'id-shaped field "author.user_id" must be a top-level row key',
139
+ rowIndex: 0,
140
+ },
141
+ ]);
142
+ });
143
+ });
144
+ describe('deriveFixture', () => {
145
+ it('returns rowCount.min=0 when rows are empty', () => {
146
+ expect(deriveFixture([])).toEqual({ expect: { rowCount: { min: 0 } } });
147
+ });
148
+ it('extracts columns from first row and infers types per column', () => {
149
+ const fixture = deriveFixture([
150
+ { id: 1, title: 'a', url: 'https://x' },
151
+ { id: 2, title: 'b', url: 'https://y' },
152
+ ]);
153
+ expect(fixture.expect?.columns).toEqual(['id', 'title', 'url']);
154
+ expect(fixture.expect?.types).toEqual({
155
+ id: 'number',
156
+ title: 'string',
157
+ url: 'string',
158
+ });
159
+ expect(fixture.expect?.rowCount).toEqual({ min: 1 });
160
+ });
161
+ it('unions mixed types across rows as "a|b"', () => {
162
+ const fixture = deriveFixture([
163
+ { v: 1 },
164
+ { v: 'two' },
165
+ { v: null },
166
+ ]);
167
+ expect(fixture.expect?.types?.v).toBe('null|number|string');
168
+ });
169
+ it('embeds args when provided', () => {
170
+ const fixture = deriveFixture([{ x: 1 }], { limit: 5 });
171
+ expect(fixture.args).toEqual({ limit: 5 });
172
+ });
173
+ it('embeds positional argv array when provided', () => {
174
+ const fixture = deriveFixture([{ x: 1 }], ['123', '--limit', '3']);
175
+ expect(fixture.args).toEqual(['123', '--limit', '3']);
176
+ });
177
+ it('does not add patterns or notEmpty automatically', () => {
178
+ const fixture = deriveFixture([{ a: 'x' }]);
179
+ expect(fixture.expect?.patterns).toBeUndefined();
180
+ expect(fixture.expect?.notEmpty).toBeUndefined();
181
+ });
182
+ });
183
+ describe('expandFixtureArgs', () => {
184
+ it('returns [] for undefined', () => {
185
+ expect(expandFixtureArgs(undefined)).toEqual([]);
186
+ });
187
+ it('expands object form as --key value pairs', () => {
188
+ expect(expandFixtureArgs({ limit: 3, sort: 'hot' })).toEqual(['--limit', '3', '--sort', 'hot']);
189
+ });
190
+ it('passes array form verbatim, stringifying values', () => {
191
+ expect(expandFixtureArgs(['123456', '--limit', 3])).toEqual(['123456', '--limit', '3']);
192
+ });
193
+ it('handles empty object and empty array', () => {
194
+ expect(expandFixtureArgs({})).toEqual([]);
195
+ expect(expandFixtureArgs([])).toEqual([]);
196
+ });
197
+ it('preserves positional + flag mix (e.g. <tid> --limit 3)', () => {
198
+ expect(expandFixtureArgs(['https://example.com/thread-1', '--comments', '5'])).toEqual([
199
+ 'https://example.com/thread-1',
200
+ '--comments',
201
+ '5',
202
+ ]);
203
+ });
204
+ });
205
+ describe('parseSeedArgs', () => {
206
+ it('treats plain text as one positional arg', () => {
207
+ expect(parseSeedArgs('webcmd-verify')).toEqual(['webcmd-verify']);
208
+ });
209
+ it('accepts JSON array seed args', () => {
210
+ expect(parseSeedArgs('["subject", "--limit", 3]')).toEqual(['subject', '--limit', 3]);
211
+ });
212
+ it('accepts JSON object seed args', () => {
213
+ expect(parseSeedArgs('{"limit":3,"sort":"hot"}')).toEqual({ limit: 3, sort: 'hot' });
214
+ });
215
+ it('ignores empty input', () => {
216
+ expect(parseSeedArgs(undefined)).toBeUndefined();
217
+ expect(parseSeedArgs(' ')).toBeUndefined();
218
+ });
219
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Visual ref overlay for annotated screenshots.
3
+ *
4
+ * The overlay is intentionally DOM-side and temporary. It reuses the same
5
+ * `data-webcmd-ref` attributes produced by the DOM snapshot path so the
6
+ * screenshot labels map back to normal `browser click <ref>` targets.
7
+ */
8
+ export declare function installVisualRefOverlayJs(opts?: {
9
+ maxRefs?: number;
10
+ }): string;
11
+ export declare function removeVisualRefOverlayJs(): string;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Visual ref overlay for annotated screenshots.
3
+ *
4
+ * The overlay is intentionally DOM-side and temporary. It reuses the same
5
+ * `data-webcmd-ref` attributes produced by the DOM snapshot path so the
6
+ * screenshot labels map back to normal `browser click <ref>` targets.
7
+ */
8
+ const OVERLAY_ID = '__webcmd_visual_ref_overlay';
9
+ export function installVisualRefOverlayJs(opts = {}) {
10
+ const maxRefs = Math.max(1, Math.min(opts.maxRefs ?? 120, 500));
11
+ return `
12
+ (() => {
13
+ const OVERLAY_ID = ${JSON.stringify(OVERLAY_ID)};
14
+ const MAX_REFS = ${maxRefs};
15
+ document.getElementById(OVERLAY_ID)?.remove();
16
+
17
+ const overlay = document.createElement('div');
18
+ overlay.id = OVERLAY_ID;
19
+ overlay.setAttribute('aria-hidden', 'true');
20
+ Object.assign(overlay.style, {
21
+ position: 'fixed',
22
+ inset: '0',
23
+ zIndex: '2147483647',
24
+ pointerEvents: 'none',
25
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
26
+ });
27
+
28
+ const refs = Array.from(document.querySelectorAll('[data-webcmd-ref]'))
29
+ .map((el) => {
30
+ const rawRef = el.getAttribute('data-webcmd-ref') || '';
31
+ const ref = Number(rawRef);
32
+ const rect = el.getBoundingClientRect();
33
+ const visible = Number.isFinite(ref)
34
+ && rect.width >= 2
35
+ && rect.height >= 2
36
+ && rect.bottom >= 0
37
+ && rect.right >= 0
38
+ && rect.top <= window.innerHeight
39
+ && rect.left <= window.innerWidth;
40
+ return { el, rawRef, ref, rect, visible };
41
+ })
42
+ .filter((entry) => entry.visible)
43
+ .sort((a, b) => a.ref - b.ref)
44
+ .slice(0, MAX_REFS);
45
+
46
+ for (const entry of refs) {
47
+ const left = Math.max(0, Math.min(window.innerWidth - 1, entry.rect.left));
48
+ const top = Math.max(0, Math.min(window.innerHeight - 1, entry.rect.top));
49
+ const right = Math.max(0, Math.min(window.innerWidth, entry.rect.right));
50
+ const bottom = Math.max(0, Math.min(window.innerHeight, entry.rect.bottom));
51
+ const width = Math.max(2, right - left);
52
+ const height = Math.max(2, bottom - top);
53
+
54
+ const box = document.createElement('div');
55
+ Object.assign(box.style, {
56
+ position: 'fixed',
57
+ left: left + 'px',
58
+ top: top + 'px',
59
+ width: width + 'px',
60
+ height: height + 'px',
61
+ border: '2px solid #ff3b30',
62
+ borderRadius: '4px',
63
+ boxSizing: 'border-box',
64
+ boxShadow: '0 0 0 1px rgba(255,255,255,.9), 0 4px 16px rgba(0,0,0,.25)',
65
+ background: 'rgba(255,59,48,.08)',
66
+ });
67
+
68
+ const badge = document.createElement('div');
69
+ badge.textContent = entry.rawRef;
70
+ Object.assign(badge.style, {
71
+ position: 'fixed',
72
+ left: left + 'px',
73
+ top: Math.max(0, top - 20) + 'px',
74
+ minWidth: '18px',
75
+ height: '18px',
76
+ padding: '0 5px',
77
+ borderRadius: '999px',
78
+ border: '1px solid rgba(255,255,255,.95)',
79
+ background: '#ff3b30',
80
+ color: '#fff',
81
+ fontSize: '12px',
82
+ fontWeight: '700',
83
+ lineHeight: '18px',
84
+ textAlign: 'center',
85
+ textShadow: '0 1px 1px rgba(0,0,0,.25)',
86
+ boxShadow: '0 2px 8px rgba(0,0,0,.35)',
87
+ });
88
+
89
+ overlay.appendChild(box);
90
+ overlay.appendChild(badge);
91
+ }
92
+
93
+ document.documentElement.appendChild(overlay);
94
+ return {
95
+ annotated: refs.length,
96
+ truncated: refs.length >= MAX_REFS,
97
+ };
98
+ })()
99
+ `.trim();
100
+ }
101
+ export function removeVisualRefOverlayJs() {
102
+ return `
103
+ (() => {
104
+ document.getElementById(${JSON.stringify(OVERLAY_ID)})?.remove();
105
+ return true;
106
+ })()
107
+ `.trim();
108
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,297 @@
1
+ import { afterEach, describe, it, expect, vi } from 'vitest';
2
+ import { BrowserBridge, generateStealthJs } from './browser/index.js';
3
+ import { extractTabEntries, diffTabIndexes, appendLimited } from './browser/tabs.js';
4
+ import { withTimeoutMs } from './runtime.js';
5
+ import { __test__ as cdpTest } from './browser/cdp.js';
6
+ import { classifyBrowserError } from './browser/errors.js';
7
+ import * as daemonTransport from './browser/daemon-transport.js';
8
+ import * as daemonLifecycle from './browser/daemon-lifecycle.js';
9
+ afterEach(() => {
10
+ vi.restoreAllMocks();
11
+ });
12
+ describe('browser helpers', () => {
13
+ it('extracts tab entries from string snapshots', () => {
14
+ const entries = extractTabEntries('Tab 0 https://example.com\nTab 1 Chrome Extension');
15
+ expect(entries).toEqual([
16
+ { index: 0, identity: 'https://example.com' },
17
+ { index: 1, identity: 'Chrome Extension' },
18
+ ]);
19
+ });
20
+ it('extracts tab entries from MCP markdown format', () => {
21
+ const entries = extractTabEntries('- 0: (current) [Playwright MCP extension](chrome-extension://abc/connect.html)\n- 1: [知乎 - 首页](https://www.zhihu.com/)');
22
+ expect(entries).toEqual([
23
+ { index: 0, identity: '(current) [Playwright MCP extension](chrome-extension://abc/connect.html)' },
24
+ { index: 1, identity: '[知乎 - 首页](https://www.zhihu.com/)' },
25
+ ]);
26
+ });
27
+ it('closes only tabs that were opened during the session', () => {
28
+ const tabsToClose = diffTabIndexes(['https://example.com', 'Chrome Extension'], [
29
+ { index: 0, identity: 'https://example.com' },
30
+ { index: 1, identity: 'Chrome Extension' },
31
+ { index: 2, identity: 'https://target.example/page' },
32
+ { index: 3, identity: 'chrome-extension://bridge' },
33
+ ]);
34
+ expect(tabsToClose).toEqual([3, 2]);
35
+ });
36
+ it('keeps only the tail of stderr buffers', () => {
37
+ expect(appendLimited('12345', '67890', 8)).toBe('34567890');
38
+ });
39
+ it('times out slow promises', async () => {
40
+ await expect(withTimeoutMs(new Promise(() => { }), 10, 'timeout')).rejects.toThrow('timeout');
41
+ });
42
+ it('classifies browser errors with correct kind and retry advice', () => {
43
+ // CDP target navigation — page-level settle retry
44
+ const nav = classifyBrowserError(new Error('{"code":-32000,"message":"Inspected target navigated or closed"}'));
45
+ expect(nav.kind).toBe('target-navigation');
46
+ expect(nav.delayMs).toBe(200);
47
+ // Extension transient — daemon-client retry only, NOT page-level
48
+ const ext = classifyBrowserError(new Error('Extension disconnected'));
49
+ expect(ext.kind).toBe('extension-transient');
50
+ expect(ext.delayMs).toBe(1500);
51
+ // Non-transient errors — not retryable
52
+ expect(classifyBrowserError(new Error('malformed exec payload')).kind).toBe('non-retryable');
53
+ expect(classifyBrowserError(new Error('Permission denied')).kind).toBe('non-retryable');
54
+ });
55
+ it('prefers the real Electron app target over DevTools and blank pages', () => {
56
+ const target = cdpTest.selectCDPTarget([
57
+ {
58
+ type: 'page',
59
+ title: 'DevTools - localhost:9224',
60
+ url: 'devtools://devtools/bundled/inspector.html',
61
+ webSocketDebuggerUrl: 'ws://127.0.0.1:9224/devtools',
62
+ },
63
+ {
64
+ type: 'page',
65
+ title: '',
66
+ url: 'about:blank',
67
+ webSocketDebuggerUrl: 'ws://127.0.0.1:9224/blank',
68
+ },
69
+ {
70
+ type: 'app',
71
+ title: 'Antigravity',
72
+ url: 'http://localhost:3000/',
73
+ webSocketDebuggerUrl: 'ws://127.0.0.1:9224/app',
74
+ },
75
+ ]);
76
+ expect(target?.webSocketDebuggerUrl).toBe('ws://127.0.0.1:9224/app');
77
+ });
78
+ it('honors WEBCMD_CDP_TARGET when multiple inspectable targets exist', () => {
79
+ vi.stubEnv('WEBCMD_CDP_TARGET', 'codex');
80
+ const target = cdpTest.selectCDPTarget([
81
+ {
82
+ type: 'app',
83
+ title: 'Cursor',
84
+ url: 'http://localhost:3000/cursor',
85
+ webSocketDebuggerUrl: 'ws://127.0.0.1:9226/cursor',
86
+ },
87
+ {
88
+ type: 'app',
89
+ title: 'OpenAI Codex',
90
+ url: 'http://localhost:3000/codex',
91
+ webSocketDebuggerUrl: 'ws://127.0.0.1:9226/codex',
92
+ },
93
+ ]);
94
+ expect(target?.webSocketDebuggerUrl).toBe('ws://127.0.0.1:9226/codex');
95
+ });
96
+ });
97
+ describe('BrowserBridge state', () => {
98
+ it('transitions to closed after close()', async () => {
99
+ const bridge = new BrowserBridge();
100
+ expect(bridge.state).toBe('idle');
101
+ await bridge.close();
102
+ expect(bridge.state).toBe('closed');
103
+ });
104
+ it('rejects connect() after the session has been closed', async () => {
105
+ const bridge = new BrowserBridge();
106
+ await bridge.close();
107
+ await expect(bridge.connect()).rejects.toThrow('Session is closed');
108
+ });
109
+ it('rejects connect() while already connecting', async () => {
110
+ const bridge = new BrowserBridge();
111
+ bridge._state = 'connecting';
112
+ await expect(bridge.connect()).rejects.toThrow('Already connecting');
113
+ });
114
+ it('rejects connect() while closing', async () => {
115
+ const bridge = new BrowserBridge();
116
+ bridge._state = 'closing';
117
+ await expect(bridge.connect()).rejects.toThrow('Session is closing');
118
+ });
119
+ it('fails fast when daemon is running but runtime is disconnected (same version)', async () => {
120
+ const { PKG_VERSION } = await import('./version.js');
121
+ vi.spyOn(daemonTransport, 'getDaemonHealth').mockResolvedValue({
122
+ state: 'no-runtime',
123
+ status: {
124
+ ok: true,
125
+ pid: 999999,
126
+ uptime: 0,
127
+ daemonVersion: PKG_VERSION,
128
+ runtimeConnected: false,
129
+ runtimeName: 'fake',
130
+ pending: 0,
131
+ memoryMB: 0,
132
+ port: 0,
133
+ },
134
+ });
135
+ const bridge = new BrowserBridge();
136
+ await expect(bridge.connect({ timeout: 0.1 })).rejects.toThrow('Browser runtime is not ready');
137
+ });
138
+ it('attempts stale daemon replacement when daemonVersion is missing', async () => {
139
+ vi.spyOn(daemonTransport, 'getDaemonHealth').mockResolvedValue({
140
+ state: 'no-runtime',
141
+ status: {
142
+ ok: true,
143
+ pid: 999999,
144
+ uptime: 0,
145
+ runtimeConnected: false,
146
+ runtimeName: 'fake',
147
+ pending: 0,
148
+ memoryMB: 0,
149
+ port: 0,
150
+ },
151
+ });
152
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'requestDaemonShutdown').mockResolvedValue(false);
153
+ // Keep the SIGKILL fallback's poll short — neither pid 999999 nor the test
154
+ // daemon exists, so we'd otherwise spin for 2s on every test in the block.
155
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'waitForDaemonStop').mockResolvedValue(false);
156
+ const bridge = new BrowserBridge();
157
+ await expect(bridge.connect({ timeout: 0.1 })).rejects.toThrow('Stale daemon could not be replaced');
158
+ });
159
+ it('attempts stale daemon replacement when daemonVersion mismatches', async () => {
160
+ vi.spyOn(daemonTransport, 'getDaemonHealth').mockResolvedValue({
161
+ state: 'no-runtime',
162
+ status: {
163
+ ok: true,
164
+ pid: 999999,
165
+ uptime: 0,
166
+ daemonVersion: '0.0.1',
167
+ runtimeConnected: false,
168
+ runtimeName: 'fake',
169
+ pending: 0,
170
+ memoryMB: 0,
171
+ port: 0,
172
+ },
173
+ });
174
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'requestDaemonShutdown').mockResolvedValue(false);
175
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'waitForDaemonStop').mockResolvedValue(false);
176
+ const bridge = new BrowserBridge();
177
+ await expect(bridge.connect({ timeout: 0.1 })).rejects.toThrow('Stale daemon could not be replaced');
178
+ });
179
+ it('attempts stale daemon replacement even when runtime is connected', async () => {
180
+ vi.spyOn(daemonTransport, 'getDaemonHealth').mockResolvedValue({
181
+ state: 'ready',
182
+ status: {
183
+ ok: true,
184
+ pid: 999999,
185
+ uptime: 0,
186
+ daemonVersion: '0.0.1',
187
+ runtimeConnected: true,
188
+ runtimeName: 'fake',
189
+ pending: 0,
190
+ memoryMB: 0,
191
+ port: 0,
192
+ },
193
+ });
194
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'requestDaemonShutdown').mockResolvedValue(false);
195
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'waitForDaemonStop').mockResolvedValue(false);
196
+ const bridge = new BrowserBridge();
197
+ await expect(bridge.connect({ timeout: 0.1 })).rejects.toThrow('Stale daemon could not be replaced');
198
+ });
199
+ it('falls back to SIGKILL when stale daemon refuses graceful shutdown', async () => {
200
+ vi.spyOn(daemonTransport, 'getDaemonHealth').mockResolvedValue({
201
+ state: 'no-runtime',
202
+ status: {
203
+ ok: true,
204
+ pid: 99999,
205
+ uptime: 0,
206
+ daemonVersion: '0.0.1',
207
+ runtimeConnected: false,
208
+ runtimeName: 'fake',
209
+ pending: 0,
210
+ memoryMB: 0,
211
+ port: 0,
212
+ },
213
+ });
214
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'requestDaemonShutdown').mockResolvedValue(false);
215
+ // Graceful shutdown short-circuits to false (requestDaemonShutdown -> false).
216
+ // After SIGKILL the port is released, so the second waitForDaemonStop returns true.
217
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'waitForDaemonStop').mockResolvedValue(true);
218
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'spawnDaemonProcess').mockReturnValue(null);
219
+ const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => true);
220
+ const bridge = new BrowserBridge();
221
+ // We expect the stale-daemon path to succeed and then continue into the
222
+ // no-runtime wait (which times out with `timeout: 0.1`), producing the
223
+ // runtime-not-ready error rather than the stale-daemon error.
224
+ await expect(bridge.connect({ timeout: 0.1 })).rejects.toThrow('Browser runtime is not ready');
225
+ expect(killSpy).toHaveBeenCalledWith(99999, 'SIGKILL');
226
+ });
227
+ it('reports stale daemon error when SIGKILL fails to release the port', async () => {
228
+ vi.spyOn(daemonTransport, 'getDaemonHealth').mockResolvedValue({
229
+ state: 'no-runtime',
230
+ status: {
231
+ ok: true,
232
+ pid: 99999,
233
+ uptime: 0,
234
+ daemonVersion: '0.0.1',
235
+ runtimeConnected: false,
236
+ runtimeName: 'fake',
237
+ pending: 0,
238
+ memoryMB: 0,
239
+ port: 0,
240
+ },
241
+ });
242
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'requestDaemonShutdown').mockResolvedValue(false);
243
+ // Graceful + SIGKILL both fail to release the port.
244
+ vi.spyOn(daemonLifecycle.daemonLifecycleHooks, 'waitForDaemonStop').mockResolvedValue(false);
245
+ vi.spyOn(process, 'kill').mockImplementation(() => {
246
+ throw Object.assign(new Error('EPERM'), { code: 'EPERM' });
247
+ });
248
+ const bridge = new BrowserBridge();
249
+ await expect(bridge.connect({ timeout: 0.1 })).rejects.toThrow('Stale daemon could not be replaced');
250
+ });
251
+ });
252
+ describe('stealth anti-detection', () => {
253
+ it('generates non-empty JS string', () => {
254
+ const js = generateStealthJs();
255
+ expect(typeof js).toBe('string');
256
+ expect(js.length).toBeGreaterThan(100);
257
+ });
258
+ it('contains all 7 anti-detection patches', () => {
259
+ const js = generateStealthJs();
260
+ // 1. webdriver
261
+ expect(js).toContain('navigator');
262
+ expect(js).toContain('webdriver');
263
+ // 2. chrome stub
264
+ expect(js).toContain('window.chrome');
265
+ // 3. plugins
266
+ expect(js).toContain('plugins');
267
+ expect(js).toContain('PDF Viewer');
268
+ // 4. languages
269
+ expect(js).toContain('languages');
270
+ // 5. permissions
271
+ expect(js).toContain('Permissions');
272
+ expect(js).toContain('notifications');
273
+ // 6. automation artifacts (dynamic cdc_ scan)
274
+ expect(js).toContain('__playwright');
275
+ expect(js).toContain('__puppeteer');
276
+ expect(js).toContain('getOwnPropertyNames');
277
+ expect(js).toContain('cdc_');
278
+ // 7. CDP stack trace cleanup
279
+ expect(js).toContain('Error.prototype');
280
+ expect(js).toContain('puppeteer_evaluation_script');
281
+ expect(js).toContain('getOwnPropertyDescriptor');
282
+ });
283
+ it('includes guard flag to prevent double-injection', () => {
284
+ const js = generateStealthJs();
285
+ // Guard uses a non-enumerable property on a built-in prototype
286
+ expect(js).toContain("EventTarget.prototype");
287
+ // Guard should check early and return 'skipped'
288
+ expect(js).toContain("return 'skipped'");
289
+ // Normal path returns 'applied'
290
+ expect(js).toContain("return 'applied'");
291
+ });
292
+ it('generates syntactically valid JS', () => {
293
+ const js = generateStealthJs();
294
+ // Should not throw when parsed
295
+ expect(() => new Function(js)).not.toThrow();
296
+ });
297
+ });