@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,252 @@
1
+ /**
2
+ * Pipeline step: download — file download with concurrency and progress.
3
+ *
4
+ * Supports:
5
+ * - Direct HTTP downloads (images, documents)
6
+ * - yt-dlp integration for video platforms
7
+ * - Browser cookie forwarding for authenticated downloads
8
+ * - Filename templating and deduplication
9
+ */
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import { render } from '../template.js';
13
+ import { getErrorMessage } from '../../errors.js';
14
+ import { httpDownload, ytdlpDownload, saveDocument, detectContentType, requiresYtdlp, sanitizeFilename, generateFilename, exportCookiesToNetscape, getTempDir, formatCookieHeader, } from '../../download/index.js';
15
+ import { DownloadProgressTracker, formatBytes } from '../../download/progress.js';
16
+ import { mapConcurrent } from '../../utils.js';
17
+ /**
18
+ * Extract cookies from browser page.
19
+ */
20
+ async function extractBrowserCookies(page, domain) {
21
+ try {
22
+ const cookies = await page.getCookies({ domain });
23
+ return formatCookieHeader(cookies);
24
+ }
25
+ catch {
26
+ return '';
27
+ }
28
+ }
29
+ /**
30
+ * Extract cookies as array for yt-dlp Netscape format.
31
+ */
32
+ async function extractCookiesArray(page, domain) {
33
+ try {
34
+ const cookies = await page.getCookies({ domain });
35
+ return cookies
36
+ .filter((cookie) => cookie.name)
37
+ .map((cookie) => ({
38
+ name: cookie.name,
39
+ value: cookie.value,
40
+ domain: cookie.domain,
41
+ path: cookie.path ?? '/',
42
+ secure: cookie.secure ?? false,
43
+ httpOnly: cookie.httpOnly ?? false,
44
+ }));
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ }
50
+ function dedupeCookies(cookies) {
51
+ const deduped = new Map();
52
+ for (const cookie of cookies) {
53
+ deduped.set(`${cookie.domain}\t${cookie.path}\t${cookie.name}`, cookie);
54
+ }
55
+ return [...deduped.values()];
56
+ }
57
+ export async function stepDownload(page, params, data, args) {
58
+ // Parse parameters with defaults
59
+ const p = typeof params === 'object' && params !== null ? params : {};
60
+ const urlTemplate = typeof params === 'string' ? params : (p.url ?? '');
61
+ const dirTemplate = p.dir ?? './downloads';
62
+ const filenameTemplate = p.filename ?? '';
63
+ const concurrency = typeof p.concurrency === 'number' ? p.concurrency : 3;
64
+ const skipExisting = p.skip_existing !== false;
65
+ const timeout = typeof p.timeout === 'number' ? p.timeout * 1000 : 30000;
66
+ const useYtdlp = p.use_ytdlp ?? false;
67
+ const ytdlpArgs = Array.isArray(p.ytdlp_args)
68
+ ? p.ytdlp_args.map((v) => String(v))
69
+ : [];
70
+ const contentType = p.type ?? 'auto';
71
+ const showProgress = p.progress !== false;
72
+ const contentTemplate = p.content;
73
+ const metadataTemplate = p.metadata;
74
+ // Resolve output directory
75
+ const dir = String(render(dirTemplate, { args, data }));
76
+ fs.mkdirSync(dir, { recursive: true });
77
+ // Normalize data to array. Items are row records (string-keyed) produced by
78
+ // upstream steps; we treat them as Record<string, unknown> and narrow per-use.
79
+ const items = Array.isArray(data) ? data
80
+ : data ? [data]
81
+ : [];
82
+ if (items.length === 0) {
83
+ return [];
84
+ }
85
+ // Create progress tracker
86
+ const tracker = new DownloadProgressTracker(items.length, showProgress);
87
+ // Cache cookie lookups per domain so mixed-domain batches stay isolated without repeated browser calls.
88
+ const cookieHeaderCache = new Map();
89
+ let cookiesFile;
90
+ if (page) {
91
+ // For yt-dlp, we need to export cookies to Netscape format
92
+ if (useYtdlp || items.some((item, index) => {
93
+ const url = String(render(urlTemplate, { args, data, item, index }));
94
+ return requiresYtdlp(url);
95
+ })) {
96
+ try {
97
+ const ytdlpDomains = [...new Set(items.flatMap((item, index) => {
98
+ const url = String(render(urlTemplate, { args, data, item, index }));
99
+ if (!useYtdlp && !requiresYtdlp(url))
100
+ return [];
101
+ try {
102
+ return [new URL(url).hostname];
103
+ }
104
+ catch {
105
+ return [];
106
+ }
107
+ }))];
108
+ const cookiesArray = dedupeCookies((await Promise.all(ytdlpDomains.map((domain) => extractCookiesArray(page, domain)))).flat());
109
+ if (cookiesArray.length > 0) {
110
+ const tempDir = getTempDir();
111
+ fs.mkdirSync(tempDir, { recursive: true });
112
+ cookiesFile = path.join(tempDir, `cookies_${Date.now()}.txt`);
113
+ exportCookiesToNetscape(cookiesArray, cookiesFile);
114
+ }
115
+ }
116
+ catch {
117
+ // Ignore cookie extraction errors
118
+ }
119
+ }
120
+ }
121
+ const results = await mapConcurrent(items, concurrency, async (item, index) => {
122
+ const startTime = Date.now();
123
+ // Render URL
124
+ const url = String(render(urlTemplate, { args, data, item, index }));
125
+ if (!url) {
126
+ tracker.onFileComplete(false);
127
+ return {
128
+ ...item,
129
+ _download: { status: 'failed', error: 'Empty URL' },
130
+ };
131
+ }
132
+ // Render filename
133
+ let filename;
134
+ if (filenameTemplate) {
135
+ filename = String(render(filenameTemplate, { args, data, item, index }));
136
+ }
137
+ else {
138
+ filename = generateFilename(url, index);
139
+ }
140
+ filename = sanitizeFilename(filename);
141
+ const destPath = path.join(dir, filename);
142
+ // Check if file exists and skip_existing is true
143
+ if (skipExisting && fs.existsSync(destPath)) {
144
+ tracker.onFileComplete(true, true);
145
+ return {
146
+ ...item,
147
+ _download: {
148
+ status: 'skipped',
149
+ path: destPath,
150
+ size: fs.statSync(destPath).size,
151
+ },
152
+ };
153
+ }
154
+ // Create progress bar for this file
155
+ const progressBar = tracker.onFileStart(filename, index);
156
+ // Determine download method
157
+ const detectedType = contentType === 'auto' ? detectContentType(url) : contentType;
158
+ const shouldUseYtdlp = useYtdlp || (detectedType === 'video' && requiresYtdlp(url));
159
+ let result;
160
+ try {
161
+ if (detectedType === 'document' && contentTemplate) {
162
+ // Save extracted content as document
163
+ const content = String(render(contentTemplate, { args, data, item, index }));
164
+ const metadata = metadataTemplate
165
+ ? Object.fromEntries(Object.entries(metadataTemplate).map(([k, v]) => [k, render(v, { args, data, item, index })]))
166
+ : undefined;
167
+ const ext = path.extname(filename).toLowerCase();
168
+ const format = ext === '.json' ? 'json' : ext === '.html' ? 'html' : 'markdown';
169
+ result = await saveDocument(content, destPath, format, metadata);
170
+ if (progressBar) {
171
+ progressBar.complete(result.success, result.success ? formatBytes(result.size) : undefined);
172
+ }
173
+ }
174
+ else if (shouldUseYtdlp) {
175
+ // Use yt-dlp for video downloads
176
+ result = await ytdlpDownload(url, destPath, {
177
+ cookiesFile,
178
+ extraArgs: ytdlpArgs,
179
+ onProgress: (percent) => {
180
+ if (progressBar) {
181
+ progressBar.update(percent, 100);
182
+ }
183
+ },
184
+ });
185
+ if (progressBar) {
186
+ progressBar.complete(result.success, result.success ? formatBytes(result.size) : undefined);
187
+ }
188
+ }
189
+ else {
190
+ // Direct HTTP download
191
+ let cookies = '';
192
+ if (page) {
193
+ try {
194
+ const targetDomain = new URL(url).hostname;
195
+ let cookiePromise = cookieHeaderCache.get(targetDomain);
196
+ if (!cookiePromise) {
197
+ cookiePromise = extractBrowserCookies(page, targetDomain);
198
+ cookieHeaderCache.set(targetDomain, cookiePromise);
199
+ }
200
+ cookies = await cookiePromise;
201
+ }
202
+ catch {
203
+ cookies = '';
204
+ }
205
+ }
206
+ result = await httpDownload(url, destPath, {
207
+ cookies,
208
+ timeout,
209
+ onProgress: (received, total) => {
210
+ if (progressBar) {
211
+ progressBar.update(received, total);
212
+ }
213
+ },
214
+ });
215
+ if (progressBar) {
216
+ progressBar.complete(result.success, result.success ? formatBytes(result.size) : undefined);
217
+ }
218
+ }
219
+ }
220
+ catch (err) {
221
+ const msg = getErrorMessage(err);
222
+ result = { success: false, size: 0, error: msg };
223
+ if (progressBar) {
224
+ progressBar.fail(msg);
225
+ }
226
+ }
227
+ tracker.onFileComplete(result.success);
228
+ const duration = Date.now() - startTime;
229
+ return {
230
+ ...item,
231
+ _download: {
232
+ status: result.success ? 'success' : 'failed',
233
+ path: result.success ? destPath : undefined,
234
+ size: result.size,
235
+ error: result.error,
236
+ duration,
237
+ },
238
+ };
239
+ });
240
+ // Cleanup temp cookie file
241
+ if (cookiesFile && fs.existsSync(cookiesFile)) {
242
+ try {
243
+ fs.unlinkSync(cookiesFile);
244
+ }
245
+ catch {
246
+ // Ignore cleanup errors
247
+ }
248
+ }
249
+ // Show summary
250
+ tracker.finish();
251
+ return results;
252
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,102 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ const { mockHttpDownload, mockYtdlpDownload, mockExportCookiesToNetscape } = vi.hoisted(() => ({
5
+ mockHttpDownload: vi.fn(),
6
+ mockYtdlpDownload: vi.fn(),
7
+ mockExportCookiesToNetscape: vi.fn(),
8
+ }));
9
+ vi.mock('../../download/index.js', async () => {
10
+ const actual = await vi.importActual('../../download/index.js');
11
+ return {
12
+ ...actual,
13
+ httpDownload: mockHttpDownload,
14
+ ytdlpDownload: mockYtdlpDownload,
15
+ exportCookiesToNetscape: mockExportCookiesToNetscape,
16
+ };
17
+ });
18
+ import { stepDownload } from './download.js';
19
+ function createMockPage(getCookies) {
20
+ return {
21
+ goto: vi.fn(),
22
+ evaluate: vi.fn().mockResolvedValue(null),
23
+ fetchJson: vi.fn().mockResolvedValue(null),
24
+ getCookies,
25
+ snapshot: vi.fn().mockResolvedValue(''),
26
+ click: vi.fn(),
27
+ typeText: vi.fn(),
28
+ fillText: vi.fn(),
29
+ pressKey: vi.fn(),
30
+ scrollTo: vi.fn(),
31
+ getFormState: vi.fn().mockResolvedValue({}),
32
+ wait: vi.fn(),
33
+ tabs: vi.fn().mockResolvedValue([]),
34
+ selectTab: vi.fn(),
35
+ networkRequests: vi.fn().mockResolvedValue([]),
36
+ consoleMessages: vi.fn().mockResolvedValue([]),
37
+ scroll: vi.fn(),
38
+ autoScroll: vi.fn(),
39
+ installInterceptor: vi.fn(),
40
+ getInterceptedRequests: vi.fn().mockResolvedValue([]),
41
+ screenshot: vi.fn().mockResolvedValue(''),
42
+ waitForCapture: vi.fn().mockResolvedValue(undefined),
43
+ };
44
+ }
45
+ describe('stepDownload', () => {
46
+ beforeEach(() => {
47
+ mockHttpDownload.mockReset();
48
+ mockHttpDownload.mockResolvedValue({ success: true, size: 2 });
49
+ mockYtdlpDownload.mockReset();
50
+ mockYtdlpDownload.mockResolvedValue({ success: true, size: 2 });
51
+ mockExportCookiesToNetscape.mockReset();
52
+ });
53
+ it('scopes browser cookies to each direct-download target domain', async () => {
54
+ const page = createMockPage(vi.fn().mockImplementation(async (opts) => {
55
+ const domain = opts?.domain ?? 'unknown';
56
+ return [{ name: 'sid', value: domain, domain }];
57
+ }));
58
+ await stepDownload(page, {
59
+ url: '${{ item.url }}',
60
+ dir: path.join(os.tmpdir(), 'webcmd-download-test'),
61
+ filename: '${{ index }}.txt',
62
+ progress: false,
63
+ concurrency: 1,
64
+ }, [
65
+ { url: 'https://a.example/file-1.txt' },
66
+ { url: 'https://b.example/file-2.txt' },
67
+ ], {});
68
+ expect(mockHttpDownload).toHaveBeenNthCalledWith(1, 'https://a.example/file-1.txt', path.join(os.tmpdir(), 'webcmd-download-test', '0.txt'), expect.objectContaining({ cookies: 'sid=a.example' }));
69
+ expect(mockHttpDownload).toHaveBeenNthCalledWith(2, 'https://b.example/file-2.txt', path.join(os.tmpdir(), 'webcmd-download-test', '1.txt'), expect.objectContaining({ cookies: 'sid=b.example' }));
70
+ });
71
+ it('builds yt-dlp cookies from all target domains instead of only the first item', async () => {
72
+ const getCookies = vi.fn().mockImplementation(async (opts) => {
73
+ const domain = opts?.domain ?? 'unknown';
74
+ return [{
75
+ name: `sid-${domain}`,
76
+ value: domain,
77
+ domain,
78
+ path: '/',
79
+ secure: false,
80
+ httpOnly: false,
81
+ }];
82
+ });
83
+ const page = createMockPage(getCookies);
84
+ await stepDownload(page, {
85
+ url: '${{ item.url }}',
86
+ dir: '/tmp/webcmd-download-test',
87
+ filename: '${{ index }}.mp4',
88
+ progress: false,
89
+ concurrency: 1,
90
+ }, [
91
+ { url: 'https://www.youtube.com/watch?v=one' },
92
+ { url: 'https://www.bilibili.com/video/BV1xx411c7mD' },
93
+ ], {});
94
+ expect(getCookies).toHaveBeenCalledWith({ domain: 'www.youtube.com' });
95
+ expect(getCookies).toHaveBeenCalledWith({ domain: 'www.bilibili.com' });
96
+ expect(mockExportCookiesToNetscape).toHaveBeenCalledWith(expect.arrayContaining([
97
+ expect.objectContaining({ name: 'sid-www.youtube.com', domain: 'www.youtube.com' }),
98
+ expect.objectContaining({ name: 'sid-www.bilibili.com', domain: 'www.bilibili.com' }),
99
+ ]), expect.any(String));
100
+ expect(mockYtdlpDownload).toHaveBeenCalledTimes(2);
101
+ });
102
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Pipeline step: fetch — HTTP API requests.
3
+ */
4
+ import type { IPage } from '../../types.js';
5
+ export declare function stepFetch(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Pipeline step: fetch — HTTP API requests.
3
+ */
4
+ import { CliError, getErrorMessage } from '../../errors.js';
5
+ import { log } from '../../logger.js';
6
+ import { render } from '../template.js';
7
+ import { isRecord, mapConcurrent } from '../../utils.js';
8
+ /** Single URL fetch helper */
9
+ async function fetchSingle(page, url, method, queryParams, headers, args, data) {
10
+ const renderedParams = {};
11
+ for (const [k, v] of Object.entries(queryParams))
12
+ renderedParams[k] = String(render(v, { args, data }));
13
+ const renderedHeaders = {};
14
+ for (const [k, v] of Object.entries(headers))
15
+ renderedHeaders[k] = String(render(v, { args, data }));
16
+ let finalUrl = url;
17
+ if (Object.keys(renderedParams).length > 0) {
18
+ const qs = new URLSearchParams(renderedParams).toString();
19
+ finalUrl = `${finalUrl}${finalUrl.includes('?') ? '&' : '?'}${qs}`;
20
+ }
21
+ if (page === null) {
22
+ const resp = await fetch(finalUrl, { method: method.toUpperCase(), headers: renderedHeaders });
23
+ if (!resp.ok) {
24
+ throw new CliError('FETCH_ERROR', `HTTP ${resp.status} ${resp.statusText} from ${finalUrl}`);
25
+ }
26
+ return resp.json();
27
+ }
28
+ return page.fetchJson(finalUrl, { method: method.toUpperCase(), headers: renderedHeaders });
29
+ }
30
+ /**
31
+ * Batch fetch: send all URLs into the browser as a single evaluate() call.
32
+ * This eliminates N-1 cross-process IPC round trips, performing all fetches
33
+ * inside the V8 engine and returning results as one JSON array.
34
+ */
35
+ async function fetchBatchInBrowser(page, urls, method, headers, concurrency) {
36
+ const headersJs = JSON.stringify(headers);
37
+ const urlsJs = JSON.stringify(urls);
38
+ const methodJs = JSON.stringify(method);
39
+ return (await page.evaluate(`
40
+ async () => {
41
+ const urls = ${urlsJs};
42
+ const method = ${methodJs};
43
+ const headers = ${headersJs};
44
+ const concurrency = ${concurrency};
45
+
46
+ const results = new Array(urls.length);
47
+ let idx = 0;
48
+
49
+ async function worker() {
50
+ while (idx < urls.length) {
51
+ const i = idx++;
52
+ try {
53
+ const resp = await fetch(urls[i], { method, headers, credentials: "include" });
54
+ if (!resp.ok) {
55
+ throw new Error('HTTP ' + resp.status + ' ' + resp.statusText + ' from ' + urls[i]);
56
+ }
57
+ results[i] = await resp.json();
58
+ } catch (e) {
59
+ results[i] = { error: e instanceof Error ? e.message : String(e) };
60
+ // Note: getErrorMessage() is a Node.js utility — can't use it inside evaluate()
61
+ }
62
+ }
63
+ }
64
+
65
+ const workers = Array.from({ length: Math.min(concurrency, urls.length) }, () => worker());
66
+ await Promise.all(workers);
67
+ return results;
68
+ }
69
+ `));
70
+ }
71
+ export async function stepFetch(page, params, data, args) {
72
+ const paramObject = isRecord(params) ? params : {};
73
+ const urlOrObj = typeof params === 'string' ? params : (paramObject.url ?? '');
74
+ const method = typeof paramObject.method === 'string' ? paramObject.method : 'GET';
75
+ const queryParams = isRecord(paramObject.params) ? paramObject.params : {};
76
+ const headers = isRecord(paramObject.headers) ? paramObject.headers : {};
77
+ const urlTemplate = String(urlOrObj);
78
+ // Per-item fetch when data is array and URL references item
79
+ if (Array.isArray(data) && urlTemplate.includes('item')) {
80
+ const concurrency = typeof paramObject.concurrency === 'number' ? paramObject.concurrency : 5;
81
+ // Render all URLs upfront
82
+ const renderedHeaders = {};
83
+ for (const [k, v] of Object.entries(headers))
84
+ renderedHeaders[k] = String(render(v, { args, data }));
85
+ const renderedParams = {};
86
+ for (const [k, v] of Object.entries(queryParams))
87
+ renderedParams[k] = String(render(v, { args, data }));
88
+ const urls = data.map((item, index) => {
89
+ let url = String(render(urlTemplate, { args, data, item, index }));
90
+ if (Object.keys(renderedParams).length > 0) {
91
+ const qs = new URLSearchParams(renderedParams).toString();
92
+ url = `${url}${url.includes('?') ? '&' : '?'}${qs}`;
93
+ }
94
+ return url;
95
+ });
96
+ // BATCH IPC: if browser is available, batch all fetches into a single evaluate() call
97
+ if (page !== null) {
98
+ const results = await fetchBatchInBrowser(page, urls, method.toUpperCase(), renderedHeaders, concurrency);
99
+ for (let i = 0; i < results.length; i++) {
100
+ const r = results[i];
101
+ if (r && typeof r === 'object' && 'error' in r) {
102
+ log.warn(`Batch fetch failed for ${urls[i]}: ${r.error}`);
103
+ }
104
+ }
105
+ return results;
106
+ }
107
+ // Non-browser: use concurrent pool (already optimized)
108
+ return mapConcurrent(data, concurrency, async (item, index) => {
109
+ const itemUrl = String(render(urlTemplate, { args, data, item, index }));
110
+ try {
111
+ return await fetchSingle(null, itemUrl, method, queryParams, headers, args, data);
112
+ }
113
+ catch (error) {
114
+ const message = getErrorMessage(error);
115
+ log.warn(`Batch fetch failed for ${itemUrl}: ${message}`);
116
+ return { error: message };
117
+ }
118
+ });
119
+ }
120
+ const url = render(urlOrObj, { args, data });
121
+ return fetchSingle(page, String(url), method, queryParams, headers, args, data);
122
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,117 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { CliError } from '../../errors.js';
3
+ import { stepFetch } from './fetch.js';
4
+ afterEach(() => {
5
+ vi.restoreAllMocks();
6
+ vi.unstubAllGlobals();
7
+ });
8
+ describe('stepFetch', () => {
9
+ // W1 + W4: non-browser single fetch throws CliError with FETCH_ERROR code and full message
10
+ it('throws CliError with FETCH_ERROR code on non-ok responses without a browser session', async () => {
11
+ const jsonMock = vi.fn().mockResolvedValue({ error: 'rate limited' });
12
+ const fetchMock = vi.fn().mockResolvedValue({
13
+ ok: false,
14
+ status: 429,
15
+ statusText: 'Too Many Requests',
16
+ json: jsonMock,
17
+ });
18
+ vi.stubGlobal('fetch', fetchMock);
19
+ const err = await stepFetch(null, { url: 'https://api.example.com/items' }, null, {}).catch((e) => e);
20
+ expect(err).toBeInstanceOf(CliError);
21
+ expect(err.code).toBe('FETCH_ERROR');
22
+ expect(err.message).toBe('HTTP 429 Too Many Requests from https://api.example.com/items');
23
+ expect(jsonMock).not.toHaveBeenCalled();
24
+ });
25
+ // W1 + W3: browser single fetch delegates to page.fetchJson, which owns browser-context fetch errors
26
+ it('throws CliError with FETCH_ERROR code on non-ok responses inside the browser session', async () => {
27
+ const page = {
28
+ fetchJson: vi.fn().mockRejectedValue(new CliError('FETCH_ERROR', 'HTTP 401 Unauthorized from https://api.example.com/items')),
29
+ };
30
+ const err = await stepFetch(page, { url: 'https://api.example.com/items' }, null, {}).catch((e) => e);
31
+ expect(err).toBeInstanceOf(CliError);
32
+ expect(err.code).toBe('FETCH_ERROR');
33
+ expect(err.message).toBe('HTTP 401 Unauthorized from https://api.example.com/items');
34
+ expect(page.fetchJson).toHaveBeenCalledWith('https://api.example.com/items', {
35
+ method: 'GET',
36
+ headers: {},
37
+ });
38
+ });
39
+ it('returns per-item HTTP errors for batch fetches without a browser session', async () => {
40
+ const jsonMock = vi.fn().mockResolvedValue({ error: 'upstream unavailable' });
41
+ const fetchMock = vi.fn().mockResolvedValue({
42
+ ok: false,
43
+ status: 503,
44
+ statusText: 'Service Unavailable',
45
+ json: jsonMock,
46
+ });
47
+ vi.stubGlobal('fetch', fetchMock);
48
+ await expect(stepFetch(null, { url: 'https://api.example.com/items/${{ item.id }}' }, [{ id: 1 }], {})).resolves.toEqual([
49
+ { error: 'HTTP 503 Service Unavailable from https://api.example.com/items/1' },
50
+ ]);
51
+ expect(jsonMock).not.toHaveBeenCalled();
52
+ });
53
+ it('returns per-item HTTP errors for batch browser fetches', async () => {
54
+ const jsonMock = vi.fn().mockResolvedValue({ error: 'upstream unavailable' });
55
+ const fetchMock = vi.fn().mockResolvedValue({
56
+ ok: false,
57
+ status: 503,
58
+ statusText: 'Service Unavailable',
59
+ json: jsonMock,
60
+ });
61
+ vi.stubGlobal('fetch', fetchMock);
62
+ const page = {
63
+ evaluate: vi.fn(async (js) => Function(`return (${js})`)()()),
64
+ };
65
+ await expect(stepFetch(page, { url: 'https://api.example.com/items/${{ item.id }}' }, [{ id: 1 }], {})).resolves.toEqual([
66
+ { error: 'HTTP 503 Service Unavailable from https://api.example.com/items/1' },
67
+ ]);
68
+ expect(jsonMock).not.toHaveBeenCalled();
69
+ });
70
+ it('stringifies non-Error batch browser failures consistently', async () => {
71
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue('socket hang up'));
72
+ const page = {
73
+ evaluate: vi.fn(async (js) => Function(`return (${js})`)()()),
74
+ };
75
+ await expect(stepFetch(page, { url: 'https://api.example.com/items/${{ item.id }}' }, [{ id: 1 }], {})).resolves.toEqual([
76
+ { error: 'socket hang up' },
77
+ ]);
78
+ });
79
+ it('stringifies non-Error batch non-browser failures consistently', async () => {
80
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue('socket hang up'));
81
+ await expect(stepFetch(null, { url: 'https://api.example.com/items/${{ item.id }}' }, [{ id: 1 }], {})).resolves.toEqual([
82
+ { error: 'socket hang up' },
83
+ ]);
84
+ });
85
+ // W2: batch item failures emit a warning log
86
+ it('logs a warning for each failed batch item in non-browser mode', async () => {
87
+ const { log } = await import('../../logger.js');
88
+ const warnSpy = vi.spyOn(log, 'warn');
89
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
90
+ ok: false,
91
+ status: 503,
92
+ statusText: 'Service Unavailable',
93
+ json: vi.fn(),
94
+ }));
95
+ await stepFetch(null, { url: 'https://api.example.com/items/${{ item.id }}' }, [{ id: 1 }, { id: 2 }], {});
96
+ expect(warnSpy).toHaveBeenCalledTimes(2);
97
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('https://api.example.com/items/1'));
98
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('https://api.example.com/items/2'));
99
+ });
100
+ it('logs a warning for each failed batch item in browser mode', async () => {
101
+ const { log } = await import('../../logger.js');
102
+ const warnSpy = vi.spyOn(log, 'warn');
103
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
104
+ ok: false,
105
+ status: 502,
106
+ statusText: 'Bad Gateway',
107
+ json: vi.fn(),
108
+ }));
109
+ const page = {
110
+ evaluate: vi.fn(async (js) => Function(`return (${js})`)()()),
111
+ };
112
+ await stepFetch(page, { url: 'https://api.example.com/items/${{ item.id }}' }, [{ id: 1 }, { id: 2 }], {});
113
+ expect(warnSpy).toHaveBeenCalledTimes(2);
114
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('https://api.example.com/items/1'));
115
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('https://api.example.com/items/2'));
116
+ });
117
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Pipeline step: intercept — declarative XHR interception.
3
+ */
4
+ import type { IPage } from '../../types.js';
5
+ export declare function stepIntercept(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Pipeline step: intercept — declarative XHR interception.
3
+ */
4
+ import { render, normalizeEvaluateSource } from '../template.js';
5
+ export async function stepIntercept(page, params, data, args) {
6
+ const cfg = typeof params === 'object' && params !== null ? params : {};
7
+ const trigger = cfg.trigger ?? '';
8
+ const capturePattern = cfg.capture ?? '';
9
+ const timeout = cfg.timeout ?? 8;
10
+ const selectPath = cfg.select ?? null;
11
+ if (!capturePattern)
12
+ return data;
13
+ // Step 1: Inject fetch/XHR interceptor BEFORE trigger
14
+ await page.installInterceptor(capturePattern);
15
+ // Step 2: Execute the trigger action
16
+ if (trigger.startsWith('navigate:')) {
17
+ const url = render(trigger.slice('navigate:'.length), { args, data });
18
+ await page.goto(String(url));
19
+ }
20
+ else if (trigger.startsWith('evaluate:')) {
21
+ const js = trigger.slice('evaluate:'.length);
22
+ await page.evaluate(normalizeEvaluateSource(render(js, { args, data })));
23
+ }
24
+ else if (trigger.startsWith('click:')) {
25
+ const ref = render(trigger.slice('click:'.length), { args, data });
26
+ await page.click(String(ref).replace(/^@/, ''));
27
+ }
28
+ else if (trigger === 'scroll') {
29
+ await page.scroll('down');
30
+ }
31
+ // Step 3: Wait for network capture (event-driven, not fixed sleep)
32
+ await page.waitForCapture(timeout);
33
+ // Step 4: Retrieve captured data
34
+ const matchingResponses = await page.getInterceptedRequests();
35
+ // Step 5: Select from response if specified
36
+ let result = matchingResponses.length === 1 ? matchingResponses[0] :
37
+ matchingResponses.length > 1 ? matchingResponses : data;
38
+ if (selectPath && result) {
39
+ let current = result;
40
+ for (const part of String(selectPath).split('.')) {
41
+ if (current && typeof current === 'object' && !Array.isArray(current)) {
42
+ current = current[part];
43
+ }
44
+ else
45
+ break;
46
+ }
47
+ result = current ?? result;
48
+ }
49
+ return result;
50
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Pipeline step: tap — declarative Store Action Bridge.
3
+ *
4
+ * Generates a self-contained IIFE that:
5
+ * 1. Injects fetch + XHR dual interception proxy
6
+ * 2. Finds the Pinia/Vuex store and calls the action
7
+ * 3. Captures the response matching the URL pattern
8
+ * 4. Auto-cleans up interception in finally block
9
+ * 5. Returns the captured data (optionally sub-selected)
10
+ */
11
+ import type { IPage } from '../../types.js';
12
+ export declare function stepTap(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown>;