@jackwener/opencli 1.3.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (496) hide show
  1. package/.github/pull_request_template.md +3 -1
  2. package/.github/workflows/build-extension.yml +7 -1
  3. package/.github/workflows/ci.yml +29 -3
  4. package/.github/workflows/docs.yml +1 -1
  5. package/.github/workflows/e2e-headed.yml +20 -0
  6. package/.github/workflows/release.yml +1 -1
  7. package/.github/workflows/security.yml +0 -3
  8. package/CHANGELOG.md +55 -0
  9. package/CONTRIBUTING.md +6 -3
  10. package/README.md +30 -3
  11. package/README.zh-CN.md +30 -3
  12. package/SKILL.md +7 -1
  13. package/TESTING.md +1 -0
  14. package/chatwise-opencli.ps1 +82 -0
  15. package/dist/analysis.d.ts +38 -0
  16. package/dist/analysis.js +166 -0
  17. package/dist/browser/cdp.d.ts +0 -4
  18. package/dist/browser/cdp.js +53 -41
  19. package/dist/browser/cdp.test.d.ts +1 -0
  20. package/dist/browser/cdp.test.js +52 -0
  21. package/dist/browser/dom-snapshot.d.ts +2 -2
  22. package/dist/browser/dom-snapshot.js +54 -1
  23. package/dist/browser/dom-snapshot.test.js +36 -0
  24. package/dist/browser/index.d.ts +2 -2
  25. package/dist/browser/index.js +1 -1
  26. package/dist/browser/mcp.d.ts +0 -2
  27. package/dist/browser/mcp.js +2 -3
  28. package/dist/browser/page.d.ts +4 -3
  29. package/dist/browser/page.js +34 -37
  30. package/dist/browser/stealth.d.ts +0 -2
  31. package/dist/browser/stealth.js +24 -9
  32. package/dist/browser.test.js +2 -2
  33. package/dist/build-manifest.js +15 -9
  34. package/dist/build-manifest.test.js +12 -0
  35. package/dist/cascade.js +4 -2
  36. package/dist/cli-manifest.json +639 -258
  37. package/dist/cli.js +57 -29
  38. package/dist/clis/_shared/desktop-commands.d.ts +22 -0
  39. package/dist/clis/_shared/desktop-commands.js +108 -0
  40. package/dist/clis/antigravity/serve.js +5 -2
  41. package/dist/clis/arxiv/search.js +1 -1
  42. package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
  43. package/dist/clis/bilibili/dynamic.test.js +68 -0
  44. package/dist/clis/bilibili/favorite.js +4 -2
  45. package/dist/clis/bilibili/following.js +3 -2
  46. package/dist/clis/bilibili/subtitle.js +8 -7
  47. package/dist/clis/bilibili/utils.js +2 -2
  48. package/dist/clis/boss/batchgreet.js +1 -1
  49. package/dist/clis/boss/chatlist.js +1 -1
  50. package/dist/clis/boss/chatmsg.js +1 -1
  51. package/dist/clis/boss/detail.js +1 -1
  52. package/dist/clis/boss/exchange.js +1 -1
  53. package/dist/clis/boss/greet.js +1 -1
  54. package/dist/clis/boss/invite.js +1 -1
  55. package/dist/clis/boss/joblist.js +1 -1
  56. package/dist/clis/boss/mark.js +4 -3
  57. package/dist/clis/boss/recommend.js +1 -1
  58. package/dist/clis/boss/resume.js +1 -1
  59. package/dist/clis/boss/search.js +1 -1
  60. package/dist/clis/boss/send.js +5 -4
  61. package/dist/clis/boss/stats.js +1 -1
  62. package/dist/clis/chatgpt/ask.js +4 -0
  63. package/dist/clis/chatgpt/new.js +5 -1
  64. package/dist/clis/chatgpt/read.js +5 -1
  65. package/dist/clis/chatgpt/send.js +2 -1
  66. package/dist/clis/chatgpt/status.js +5 -1
  67. package/dist/clis/chatwise/ask.js +8 -2
  68. package/dist/clis/chatwise/export.js +2 -0
  69. package/dist/clis/chatwise/history.js +2 -0
  70. package/dist/clis/chatwise/model.js +8 -3
  71. package/dist/clis/chatwise/new.js +3 -18
  72. package/dist/clis/chatwise/read.js +2 -0
  73. package/dist/clis/chatwise/screenshot.js +3 -27
  74. package/dist/clis/chatwise/send.js +8 -2
  75. package/dist/clis/chatwise/shared.d.ts +2 -0
  76. package/dist/clis/chatwise/shared.js +6 -0
  77. package/dist/clis/chatwise/status.js +3 -22
  78. package/dist/clis/codex/ask.js +6 -2
  79. package/dist/clis/codex/dump.js +2 -25
  80. package/dist/clis/codex/new.js +2 -25
  81. package/dist/clis/codex/screenshot.js +2 -27
  82. package/dist/clis/codex/send.js +6 -4
  83. package/dist/clis/codex/status.js +2 -22
  84. package/dist/clis/cursor/ask.js +2 -1
  85. package/dist/clis/cursor/composer.js +2 -1
  86. package/dist/clis/cursor/dump.js +2 -25
  87. package/dist/clis/cursor/new.js +2 -18
  88. package/dist/clis/cursor/read.js +2 -1
  89. package/dist/clis/cursor/screenshot.js +1 -30
  90. package/dist/clis/cursor/send.js +2 -1
  91. package/dist/clis/cursor/status.js +2 -21
  92. package/dist/clis/dictionary/examples.yaml +25 -0
  93. package/dist/clis/dictionary/search.yaml +27 -0
  94. package/dist/clis/dictionary/synonyms.yaml +25 -0
  95. package/dist/clis/douban/book-hot.js +1 -1
  96. package/dist/clis/douban/movie-hot.js +1 -1
  97. package/dist/clis/douban/search.js +1 -1
  98. package/dist/clis/douban/utils.d.ts +4 -1
  99. package/dist/clis/douban/utils.js +156 -1
  100. package/dist/clis/doubao/ask.js +1 -1
  101. package/dist/clis/doubao/new.js +1 -1
  102. package/dist/clis/doubao/read.js +1 -1
  103. package/dist/clis/doubao/send.js +1 -1
  104. package/dist/clis/doubao/status.js +1 -1
  105. package/dist/clis/doubao-app/ask.js +1 -1
  106. package/dist/clis/doubao-app/new.js +1 -1
  107. package/dist/clis/doubao-app/read.js +1 -1
  108. package/dist/clis/doubao-app/send.js +1 -1
  109. package/dist/clis/grok/ask.d.ts +4 -0
  110. package/dist/clis/grok/ask.js +28 -10
  111. package/dist/clis/grok/ask.test.js +18 -0
  112. package/dist/clis/jd/item.d.ts +1 -0
  113. package/dist/clis/jd/item.js +96 -0
  114. package/dist/clis/jd/item.test.d.ts +1 -0
  115. package/dist/clis/jd/item.test.js +28 -0
  116. package/dist/clis/jike/feed.js +1 -1
  117. package/dist/clis/jike/search.js +1 -1
  118. package/dist/clis/linkedin/search.js +5 -4
  119. package/dist/clis/linkedin/timeline.d.ts +21 -0
  120. package/dist/clis/linkedin/timeline.js +503 -0
  121. package/dist/clis/linkedin/timeline.test.d.ts +1 -0
  122. package/dist/clis/linkedin/timeline.test.js +81 -0
  123. package/dist/clis/medium/feed.js +1 -1
  124. package/dist/clis/medium/search.js +1 -1
  125. package/dist/clis/medium/user.js +1 -1
  126. package/dist/clis/medium/{shared.js → utils.js} +2 -1
  127. package/dist/clis/pixiv/detail.yaml +49 -0
  128. package/dist/clis/pixiv/download.d.ts +7 -0
  129. package/dist/clis/pixiv/download.js +78 -0
  130. package/dist/clis/pixiv/download.test.d.ts +1 -0
  131. package/dist/clis/pixiv/download.test.js +87 -0
  132. package/dist/clis/pixiv/illusts.d.ts +8 -0
  133. package/dist/clis/pixiv/illusts.js +65 -0
  134. package/dist/clis/pixiv/illusts.test.d.ts +1 -0
  135. package/dist/clis/pixiv/illusts.test.js +99 -0
  136. package/dist/clis/pixiv/ranking.yaml +53 -0
  137. package/dist/clis/pixiv/search.d.ts +6 -0
  138. package/dist/clis/pixiv/search.js +43 -0
  139. package/dist/clis/pixiv/search.test.d.ts +1 -0
  140. package/dist/clis/pixiv/search.test.js +83 -0
  141. package/dist/clis/pixiv/test-utils.d.ts +12 -0
  142. package/dist/clis/pixiv/test-utils.js +23 -0
  143. package/dist/clis/pixiv/user.yaml +46 -0
  144. package/dist/clis/pixiv/utils.d.ts +27 -0
  145. package/dist/clis/pixiv/utils.js +49 -0
  146. package/dist/clis/reddit/comment.js +2 -1
  147. package/dist/clis/reddit/read.js +4 -3
  148. package/dist/clis/reddit/read.test.d.ts +1 -0
  149. package/dist/clis/reddit/read.test.js +28 -0
  150. package/dist/clis/reddit/save.js +2 -1
  151. package/dist/clis/reddit/saved.js +7 -3
  152. package/dist/clis/reddit/subscribe.js +2 -1
  153. package/dist/clis/reddit/upvote.js +2 -1
  154. package/dist/clis/reddit/upvoted.js +7 -3
  155. package/dist/clis/sinablog/article.js +1 -1
  156. package/dist/clis/sinablog/hot.js +1 -1
  157. package/dist/clis/sinablog/user.js +1 -1
  158. package/dist/clis/substack/feed.js +1 -1
  159. package/dist/clis/substack/publication.js +1 -1
  160. package/dist/clis/substack/search.js +3 -2
  161. package/dist/clis/substack/{shared.js → utils.js} +3 -2
  162. package/dist/clis/tiktok/search.yaml +2 -1
  163. package/dist/clis/twitter/accept.js +2 -1
  164. package/dist/clis/twitter/article.js +4 -1
  165. package/dist/clis/twitter/block.js +2 -1
  166. package/dist/clis/twitter/bookmark.js +2 -1
  167. package/dist/clis/twitter/bookmarks.js +3 -2
  168. package/dist/clis/twitter/delete.js +2 -1
  169. package/dist/clis/twitter/follow.js +2 -1
  170. package/dist/clis/twitter/followers.js +3 -2
  171. package/dist/clis/twitter/following.js +3 -2
  172. package/dist/clis/twitter/hide-reply.js +2 -1
  173. package/dist/clis/twitter/like.js +2 -1
  174. package/dist/clis/twitter/notifications.js +2 -1
  175. package/dist/clis/twitter/post.js +2 -1
  176. package/dist/clis/twitter/profile.js +5 -2
  177. package/dist/clis/twitter/reply-dm.js +2 -1
  178. package/dist/clis/twitter/reply.js +2 -1
  179. package/dist/clis/twitter/search.js +30 -13
  180. package/dist/clis/twitter/search.test.d.ts +1 -0
  181. package/dist/clis/twitter/search.test.js +104 -0
  182. package/dist/clis/twitter/thread.js +2 -2
  183. package/dist/clis/twitter/timeline.js +3 -2
  184. package/dist/clis/twitter/trending.js +3 -2
  185. package/dist/clis/twitter/unblock.js +2 -1
  186. package/dist/clis/twitter/unbookmark.js +2 -1
  187. package/dist/clis/twitter/unfollow.js +2 -1
  188. package/dist/clis/v2ex/daily.js +3 -2
  189. package/dist/clis/v2ex/me.js +3 -2
  190. package/dist/clis/v2ex/notifications.js +4 -4
  191. package/dist/clis/web/read.d.ts +16 -0
  192. package/dist/clis/web/read.js +202 -0
  193. package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
  194. package/dist/clis/xueqiu/danjuan-utils.js +126 -0
  195. package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
  196. package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
  197. package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
  198. package/dist/clis/xueqiu/fund-holdings.js +28 -0
  199. package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
  200. package/dist/clis/xueqiu/fund-snapshot.js +25 -0
  201. package/dist/clis/youtube/transcript.js +5 -4
  202. package/dist/clis/youtube/video.js +3 -2
  203. package/dist/daemon.js +7 -3
  204. package/dist/discovery.js +11 -10
  205. package/dist/doctor.js +2 -1
  206. package/dist/download/index.d.ts +4 -12
  207. package/dist/download/index.js +33 -12
  208. package/dist/download/index.test.js +79 -2
  209. package/dist/download/media-download.js +4 -2
  210. package/dist/engine.test.js +76 -4
  211. package/dist/execution.d.ts +1 -9
  212. package/dist/execution.js +56 -46
  213. package/dist/explore.js +12 -111
  214. package/dist/external-clis.yaml +0 -8
  215. package/dist/external.js +7 -5
  216. package/dist/external.test.js +4 -0
  217. package/dist/generate.d.ts +0 -9
  218. package/dist/generate.js +4 -20
  219. package/dist/hooks.d.ts +46 -0
  220. package/dist/hooks.js +56 -0
  221. package/dist/hooks.test.d.ts +4 -0
  222. package/dist/hooks.test.js +92 -0
  223. package/dist/interceptor.js +70 -23
  224. package/dist/main.js +2 -0
  225. package/dist/output.js +12 -6
  226. package/dist/pipeline/executor.js +1 -1
  227. package/dist/pipeline/steps/browser.js +1 -3
  228. package/dist/pipeline/steps/download.js +42 -26
  229. package/dist/pipeline/steps/download.test.d.ts +1 -0
  230. package/dist/pipeline/steps/download.test.js +101 -0
  231. package/dist/pipeline/steps/fetch.js +40 -22
  232. package/dist/pipeline/steps/fetch.test.d.ts +1 -0
  233. package/dist/pipeline/steps/fetch.test.js +123 -0
  234. package/dist/pipeline/steps/transform.js +2 -6
  235. package/dist/pipeline/template.js +66 -52
  236. package/dist/pipeline/template.test.js +28 -0
  237. package/dist/pipeline/transform.test.js +18 -0
  238. package/dist/plugin.d.ts +40 -1
  239. package/dist/plugin.js +214 -17
  240. package/dist/plugin.test.d.ts +1 -1
  241. package/dist/plugin.test.js +219 -3
  242. package/dist/record.js +6 -98
  243. package/dist/registry-api.d.ts +2 -0
  244. package/dist/registry-api.js +1 -0
  245. package/dist/registry.d.ts +5 -2
  246. package/dist/registry.js +1 -2
  247. package/dist/runtime.d.ts +0 -1
  248. package/dist/runtime.js +14 -4
  249. package/dist/snapshotFormatter.d.ts +7 -14
  250. package/dist/snapshotFormatter.js +38 -78
  251. package/dist/utils.d.ts +9 -0
  252. package/dist/utils.js +29 -0
  253. package/dist/validate.js +3 -5
  254. package/dist/yaml-schema.d.ts +26 -0
  255. package/dist/yaml-schema.js +5 -0
  256. package/docs/.vitepress/config.mts +3 -0
  257. package/docs/adapters/browser/dictionary.md +27 -0
  258. package/docs/adapters/browser/jd.md +27 -0
  259. package/docs/adapters/browser/linkedin.md +6 -0
  260. package/docs/adapters/browser/pixiv.md +92 -0
  261. package/docs/adapters/browser/web.md +30 -0
  262. package/docs/adapters/browser/xueqiu.md +27 -9
  263. package/docs/adapters/index.md +3 -1
  264. package/docs/comparison.md +125 -0
  265. package/docs/developer/contributing.md +21 -2
  266. package/docs/developer/testing.md +14 -8
  267. package/docs/developer/ts-adapter.md +18 -0
  268. package/docs/developer/yaml-adapter.md +16 -0
  269. package/docs/guide/plugins.md +10 -0
  270. package/docs/zh/guide/plugins.md +10 -0
  271. package/extension/dist/background.js +519 -444
  272. package/extension/manifest.json +1 -1
  273. package/extension/package.json +1 -1
  274. package/extension/src/background.test.ts +46 -1
  275. package/extension/src/background.ts +108 -33
  276. package/extension/src/cdp.ts +9 -9
  277. package/package.json +3 -2
  278. package/scripts/check-doc-coverage.sh +2 -0
  279. package/src/analysis.ts +170 -0
  280. package/src/browser/cdp.test.ts +66 -0
  281. package/src/browser/cdp.ts +59 -44
  282. package/src/browser/dom-snapshot.test.ts +42 -0
  283. package/src/browser/dom-snapshot.ts +56 -3
  284. package/src/browser/index.ts +2 -2
  285. package/src/browser/mcp.ts +2 -4
  286. package/src/browser/page.ts +34 -37
  287. package/src/browser/stealth.ts +24 -10
  288. package/src/browser.test.ts +2 -2
  289. package/src/build-manifest.test.ts +14 -0
  290. package/src/build-manifest.ts +13 -31
  291. package/src/cascade.ts +5 -3
  292. package/src/cli.ts +66 -34
  293. package/src/clis/_shared/desktop-commands.ts +121 -0
  294. package/src/clis/antigravity/serve.ts +6 -3
  295. package/src/clis/arxiv/search.ts +1 -1
  296. package/src/clis/bilibili/dynamic.test.ts +79 -0
  297. package/src/clis/bilibili/favorite.ts +5 -2
  298. package/src/clis/bilibili/following.ts +3 -2
  299. package/src/clis/bilibili/subtitle.ts +8 -7
  300. package/src/clis/bilibili/utils.ts +2 -2
  301. package/src/clis/boss/batchgreet.ts +1 -1
  302. package/src/clis/boss/chatlist.ts +1 -1
  303. package/src/clis/boss/chatmsg.ts +1 -1
  304. package/src/clis/boss/detail.ts +1 -1
  305. package/src/clis/boss/exchange.ts +1 -1
  306. package/src/clis/boss/greet.ts +1 -1
  307. package/src/clis/boss/invite.ts +1 -1
  308. package/src/clis/boss/joblist.ts +1 -1
  309. package/src/clis/boss/mark.ts +4 -3
  310. package/src/clis/boss/recommend.ts +1 -1
  311. package/src/clis/boss/resume.ts +1 -1
  312. package/src/clis/boss/search.ts +1 -1
  313. package/src/clis/boss/send.ts +5 -4
  314. package/src/clis/boss/stats.ts +1 -1
  315. package/src/clis/chatgpt/ask.ts +5 -0
  316. package/src/clis/chatgpt/new.ts +7 -2
  317. package/src/clis/chatgpt/read.ts +7 -2
  318. package/src/clis/chatgpt/send.ts +3 -2
  319. package/src/clis/chatgpt/status.ts +6 -1
  320. package/src/clis/chatwise/ask.ts +7 -2
  321. package/src/clis/chatwise/export.ts +2 -0
  322. package/src/clis/chatwise/history.ts +2 -0
  323. package/src/clis/chatwise/model.ts +7 -3
  324. package/src/clis/chatwise/new.ts +3 -20
  325. package/src/clis/chatwise/read.ts +2 -0
  326. package/src/clis/chatwise/screenshot.ts +3 -32
  327. package/src/clis/chatwise/send.ts +7 -2
  328. package/src/clis/chatwise/shared.ts +8 -0
  329. package/src/clis/chatwise/status.ts +3 -24
  330. package/src/clis/codex/ask.ts +5 -2
  331. package/src/clis/codex/dump.ts +2 -27
  332. package/src/clis/codex/new.ts +2 -28
  333. package/src/clis/codex/screenshot.ts +2 -32
  334. package/src/clis/codex/send.ts +5 -4
  335. package/src/clis/codex/status.ts +2 -24
  336. package/src/clis/cursor/ask.ts +2 -1
  337. package/src/clis/cursor/composer.ts +2 -1
  338. package/src/clis/cursor/dump.ts +2 -27
  339. package/src/clis/cursor/new.ts +2 -20
  340. package/src/clis/cursor/read.ts +2 -1
  341. package/src/clis/cursor/screenshot.ts +1 -36
  342. package/src/clis/cursor/send.ts +2 -1
  343. package/src/clis/cursor/status.ts +2 -22
  344. package/src/clis/dictionary/examples.yaml +25 -0
  345. package/src/clis/dictionary/search.yaml +27 -0
  346. package/src/clis/dictionary/synonyms.yaml +25 -0
  347. package/src/clis/douban/book-hot.ts +1 -1
  348. package/src/clis/douban/movie-hot.ts +1 -1
  349. package/src/clis/douban/search.ts +1 -1
  350. package/src/clis/douban/utils.ts +165 -1
  351. package/src/clis/doubao/ask.ts +1 -1
  352. package/src/clis/doubao/new.ts +1 -1
  353. package/src/clis/doubao/read.ts +1 -1
  354. package/src/clis/doubao/send.ts +1 -1
  355. package/src/clis/doubao/status.ts +1 -1
  356. package/src/clis/doubao-app/ask.ts +1 -1
  357. package/src/clis/doubao-app/new.ts +1 -1
  358. package/src/clis/doubao-app/read.ts +1 -1
  359. package/src/clis/doubao-app/send.ts +1 -1
  360. package/src/clis/grok/ask.test.ts +25 -0
  361. package/src/clis/grok/ask.ts +25 -12
  362. package/src/clis/jd/item.test.ts +35 -0
  363. package/src/clis/jd/item.ts +101 -0
  364. package/src/clis/jike/feed.ts +1 -1
  365. package/src/clis/jike/search.ts +1 -1
  366. package/src/clis/linkedin/search.ts +5 -4
  367. package/src/clis/linkedin/timeline.test.ts +99 -0
  368. package/src/clis/linkedin/timeline.ts +532 -0
  369. package/src/clis/medium/feed.ts +1 -1
  370. package/src/clis/medium/search.ts +1 -1
  371. package/src/clis/medium/user.ts +1 -1
  372. package/src/clis/medium/{shared.ts → utils.ts} +2 -1
  373. package/src/clis/pixiv/detail.yaml +49 -0
  374. package/src/clis/pixiv/download.test.ts +114 -0
  375. package/src/clis/pixiv/download.ts +91 -0
  376. package/src/clis/pixiv/illusts.test.ts +115 -0
  377. package/src/clis/pixiv/illusts.ts +78 -0
  378. package/src/clis/pixiv/ranking.yaml +53 -0
  379. package/src/clis/pixiv/search.test.ts +97 -0
  380. package/src/clis/pixiv/search.ts +53 -0
  381. package/src/clis/pixiv/test-utils.ts +29 -0
  382. package/src/clis/pixiv/user.yaml +46 -0
  383. package/src/clis/pixiv/utils.ts +62 -0
  384. package/src/clis/reddit/comment.ts +2 -1
  385. package/src/clis/reddit/read.test.ts +34 -0
  386. package/src/clis/reddit/read.ts +4 -3
  387. package/src/clis/reddit/save.ts +2 -1
  388. package/src/clis/reddit/saved.ts +6 -2
  389. package/src/clis/reddit/subscribe.ts +2 -1
  390. package/src/clis/reddit/upvote.ts +2 -1
  391. package/src/clis/reddit/upvoted.ts +6 -2
  392. package/src/clis/sinablog/article.ts +1 -1
  393. package/src/clis/sinablog/hot.ts +1 -1
  394. package/src/clis/sinablog/user.ts +1 -1
  395. package/src/clis/substack/feed.ts +1 -1
  396. package/src/clis/substack/publication.ts +1 -1
  397. package/src/clis/substack/search.ts +3 -2
  398. package/src/clis/substack/{shared.ts → utils.ts} +3 -2
  399. package/src/clis/tiktok/search.yaml +2 -1
  400. package/src/clis/twitter/accept.ts +2 -1
  401. package/src/clis/twitter/article.ts +3 -1
  402. package/src/clis/twitter/block.ts +2 -1
  403. package/src/clis/twitter/bookmark.ts +2 -1
  404. package/src/clis/twitter/bookmarks.ts +3 -2
  405. package/src/clis/twitter/delete.ts +2 -1
  406. package/src/clis/twitter/follow.ts +2 -1
  407. package/src/clis/twitter/followers.ts +3 -2
  408. package/src/clis/twitter/following.ts +3 -2
  409. package/src/clis/twitter/hide-reply.ts +2 -1
  410. package/src/clis/twitter/like.ts +2 -1
  411. package/src/clis/twitter/notifications.ts +2 -1
  412. package/src/clis/twitter/post.ts +2 -1
  413. package/src/clis/twitter/profile.ts +4 -2
  414. package/src/clis/twitter/reply-dm.ts +2 -1
  415. package/src/clis/twitter/reply.ts +2 -1
  416. package/src/clis/twitter/search.test.ts +113 -0
  417. package/src/clis/twitter/search.ts +38 -14
  418. package/src/clis/twitter/thread.ts +2 -2
  419. package/src/clis/twitter/timeline.ts +3 -2
  420. package/src/clis/twitter/trending.ts +3 -2
  421. package/src/clis/twitter/unblock.ts +2 -1
  422. package/src/clis/twitter/unbookmark.ts +2 -1
  423. package/src/clis/twitter/unfollow.ts +2 -1
  424. package/src/clis/v2ex/daily.ts +3 -2
  425. package/src/clis/v2ex/me.ts +3 -2
  426. package/src/clis/v2ex/notifications.ts +3 -4
  427. package/src/clis/web/read.ts +210 -0
  428. package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
  429. package/src/clis/xueqiu/danjuan-utils.ts +176 -0
  430. package/src/clis/xueqiu/fund-holdings.ts +32 -0
  431. package/src/clis/xueqiu/fund-snapshot.ts +27 -0
  432. package/src/clis/youtube/transcript.ts +5 -4
  433. package/src/clis/youtube/video.ts +3 -2
  434. package/src/daemon.ts +5 -4
  435. package/src/discovery.ts +12 -34
  436. package/src/doctor.ts +3 -2
  437. package/src/download/index.test.ts +93 -2
  438. package/src/download/index.ts +44 -23
  439. package/src/download/media-download.ts +5 -3
  440. package/src/engine.test.ts +84 -3
  441. package/src/execution.ts +62 -46
  442. package/src/explore.ts +21 -90
  443. package/src/external-clis.yaml +0 -8
  444. package/src/external.test.ts +9 -0
  445. package/src/external.ts +12 -10
  446. package/src/generate.ts +4 -41
  447. package/src/hooks.test.ts +126 -0
  448. package/src/hooks.ts +90 -0
  449. package/src/interceptor.ts +73 -23
  450. package/src/main.ts +2 -0
  451. package/src/output.ts +14 -6
  452. package/src/pipeline/executor.ts +1 -1
  453. package/src/pipeline/steps/browser.ts +1 -3
  454. package/src/pipeline/steps/download.test.ts +136 -0
  455. package/src/pipeline/steps/download.ts +47 -34
  456. package/src/pipeline/steps/fetch.test.ts +179 -0
  457. package/src/pipeline/steps/fetch.ts +39 -23
  458. package/src/pipeline/steps/transform.ts +2 -6
  459. package/src/pipeline/template.test.ts +28 -0
  460. package/src/pipeline/template.ts +67 -79
  461. package/src/pipeline/transform.test.ts +20 -0
  462. package/src/plugin.test.ts +251 -3
  463. package/src/plugin.ts +265 -21
  464. package/src/record.ts +12 -84
  465. package/src/registry-api.ts +2 -0
  466. package/src/registry.ts +7 -4
  467. package/src/runtime.ts +14 -4
  468. package/src/snapshotFormatter.ts +43 -121
  469. package/src/utils.ts +39 -0
  470. package/src/validate.ts +3 -5
  471. package/src/yaml-schema.ts +28 -0
  472. package/tests/e2e/browser-auth.test.ts +25 -0
  473. package/tests/e2e/plugin-management.test.ts +137 -0
  474. package/tests/e2e/public-commands.test.ts +34 -1
  475. package/vitest.config.ts +19 -1
  476. package/.github/workflows/pkg-pr-new.yml +0 -30
  477. package/dist/clis/douban/shared.d.ts +0 -4
  478. package/dist/clis/douban/shared.js +0 -155
  479. package/src/clis/douban/shared.ts +0 -165
  480. /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
  481. /package/dist/clis/boss/{common.js → utils.js} +0 -0
  482. /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
  483. /package/dist/clis/doubao/{common.js → utils.js} +0 -0
  484. /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
  485. /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
  486. /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
  487. /package/dist/clis/jike/{shared.js → utils.js} +0 -0
  488. /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
  489. /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
  490. /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
  491. /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
  492. /package/src/clis/boss/{common.ts → utils.ts} +0 -0
  493. /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
  494. /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
  495. /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
  496. /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
@@ -18,9 +18,10 @@ export declare class Page implements IPage {
18
18
  constructor(workspace?: string);
19
19
  /** Active tab ID, set after navigate and used in all subsequent commands */
20
20
  private _tabId;
21
- /** Helper: spread tabId into command params if we have one */
22
- private _tabOpt;
23
- private _workspaceOpt;
21
+ /** Helper: spread workspace into command params */
22
+ private _wsOpt;
23
+ /** Helper: spread workspace + tabId into command params */
24
+ private _cmdOpts;
24
25
  goto(url: string, options?: {
25
26
  waitUntil?: 'load' | 'none';
26
27
  settleMs?: number;
@@ -12,6 +12,7 @@
12
12
  import { formatSnapshot } from '../snapshotFormatter.js';
13
13
  import { sendCommand } from './daemon-client.js';
14
14
  import { wrapForEval } from './utils.js';
15
+ import { saveBase64ToFile } from '../utils.js';
15
16
  import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
16
17
  import { generateStealthJs } from './stealth.js';
17
18
  import { clickJs, typeTextJs, pressKeyJs, waitForTextJs, scrollJs, autoScrollJs, networkRequestsJs, waitForDomStableJs, } from './dom-helpers.js';
@@ -25,18 +26,21 @@ export class Page {
25
26
  }
26
27
  /** Active tab ID, set after navigate and used in all subsequent commands */
27
28
  _tabId;
28
- /** Helper: spread tabId into command params if we have one */
29
- _tabOpt() {
30
- return this._tabId !== undefined ? { tabId: this._tabId } : {};
31
- }
32
- _workspaceOpt() {
29
+ /** Helper: spread workspace into command params */
30
+ _wsOpt() {
33
31
  return { workspace: this.workspace };
34
32
  }
33
+ /** Helper: spread workspace + tabId into command params */
34
+ _cmdOpts() {
35
+ return {
36
+ workspace: this.workspace,
37
+ ...(this._tabId !== undefined && { tabId: this._tabId }),
38
+ };
39
+ }
35
40
  async goto(url, options) {
36
41
  const result = await sendCommand('navigate', {
37
42
  url,
38
- ...this._workspaceOpt(),
39
- ...this._tabOpt(),
43
+ ...this._cmdOpts(),
40
44
  });
41
45
  // Remember the tabId for subsequent exec calls
42
46
  if (result?.tabId) {
@@ -46,8 +50,7 @@ export class Page {
46
50
  try {
47
51
  await sendCommand('exec', {
48
52
  code: generateStealthJs(),
49
- ...this._workspaceOpt(),
50
- ...this._tabOpt(),
53
+ ...this._cmdOpts(),
51
54
  });
52
55
  }
53
56
  catch {
@@ -59,15 +62,14 @@ export class Page {
59
62
  const maxMs = options?.settleMs ?? 1000;
60
63
  await sendCommand('exec', {
61
64
  code: waitForDomStableJs(maxMs, Math.min(500, maxMs)),
62
- ...this._workspaceOpt(),
63
- ...this._tabOpt(),
65
+ ...this._cmdOpts(),
64
66
  });
65
67
  }
66
68
  }
67
69
  /** Close the automation window in the extension */
68
70
  async closeWindow() {
69
71
  try {
70
- await sendCommand('close-window', { ...this._workspaceOpt() });
72
+ await sendCommand('close-window', { ...this._wsOpt() });
71
73
  }
72
74
  catch {
73
75
  // Window may already be closed or daemon may be down
@@ -75,10 +77,10 @@ export class Page {
75
77
  }
76
78
  async evaluate(js) {
77
79
  const code = wrapForEval(js);
78
- return sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
80
+ return sendCommand('exec', { code, ...this._cmdOpts() });
79
81
  }
80
82
  async getCookies(opts = {}) {
81
- const result = await sendCommand('cookies', { ...this._workspaceOpt(), ...opts });
83
+ const result = await sendCommand('cookies', { ...this._wsOpt(), ...opts });
82
84
  return Array.isArray(result) ? result : [];
83
85
  }
84
86
  async snapshot(opts = {}) {
@@ -92,7 +94,7 @@ export class Page {
92
94
  bboxDedup: true,
93
95
  });
94
96
  try {
95
- const result = await sendCommand('exec', { code: snapshotJs, ...this._workspaceOpt(), ...this._tabOpt() });
97
+ const result = await sendCommand('exec', { code: snapshotJs, ...this._cmdOpts() });
96
98
  // The advanced engine already produces a clean, pruned, LLM-friendly output.
97
99
  // Do NOT pass through formatSnapshot — its format is incompatible.
98
100
  return result;
@@ -132,7 +134,7 @@ export class Page {
132
134
  return buildTree(document.body, 0);
133
135
  })()
134
136
  `;
135
- const raw = await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
137
+ const raw = await sendCommand('exec', { code, ...this._cmdOpts() });
136
138
  if (opts.raw)
137
139
  return raw;
138
140
  if (typeof raw === 'string')
@@ -141,62 +143,62 @@ export class Page {
141
143
  }
142
144
  async click(ref) {
143
145
  const code = clickJs(ref);
144
- await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
146
+ await sendCommand('exec', { code, ...this._cmdOpts() });
145
147
  }
146
148
  async typeText(ref, text) {
147
149
  const code = typeTextJs(ref, text);
148
- await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
150
+ await sendCommand('exec', { code, ...this._cmdOpts() });
149
151
  }
150
152
  async pressKey(key) {
151
153
  const code = pressKeyJs(key);
152
- await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
154
+ await sendCommand('exec', { code, ...this._cmdOpts() });
153
155
  }
154
156
  async scrollTo(ref) {
155
157
  const code = scrollToRefJs(ref);
156
- return sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
158
+ return sendCommand('exec', { code, ...this._cmdOpts() });
157
159
  }
158
160
  async getFormState() {
159
161
  const code = getFormStateJs();
160
- return (await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() }));
162
+ return (await sendCommand('exec', { code, ...this._cmdOpts() }));
161
163
  }
162
164
  async wait(options) {
163
165
  if (typeof options === 'number') {
164
166
  await new Promise(resolve => setTimeout(resolve, options * 1000));
165
167
  return;
166
168
  }
167
- if (options.time) {
169
+ if (typeof options.time === 'number') {
168
170
  await new Promise(resolve => setTimeout(resolve, options.time * 1000));
169
171
  return;
170
172
  }
171
173
  if (options.text) {
172
174
  const timeout = (options.timeout ?? 30) * 1000;
173
175
  const code = waitForTextJs(options.text, timeout);
174
- await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
176
+ await sendCommand('exec', { code, ...this._cmdOpts() });
175
177
  }
176
178
  }
177
179
  async tabs() {
178
- const result = await sendCommand('tabs', { op: 'list', ...this._workspaceOpt() });
180
+ const result = await sendCommand('tabs', { op: 'list', ...this._wsOpt() });
179
181
  return Array.isArray(result) ? result : [];
180
182
  }
181
183
  async closeTab(index) {
182
- await sendCommand('tabs', { op: 'close', ...this._workspaceOpt(), ...(index !== undefined ? { index } : {}) });
184
+ await sendCommand('tabs', { op: 'close', ...this._wsOpt(), ...(index !== undefined ? { index } : {}) });
183
185
  // Invalidate cached tabId — the closed tab might have been our active one.
184
186
  // We can't know for sure (close-by-index doesn't return tabId), so reset.
185
187
  this._tabId = undefined;
186
188
  }
187
189
  async newTab() {
188
- const result = await sendCommand('tabs', { op: 'new', ...this._workspaceOpt() });
190
+ const result = await sendCommand('tabs', { op: 'new', ...this._wsOpt() });
189
191
  if (result?.tabId)
190
192
  this._tabId = result.tabId;
191
193
  }
192
194
  async selectTab(index) {
193
- const result = await sendCommand('tabs', { op: 'select', index, ...this._workspaceOpt() });
195
+ const result = await sendCommand('tabs', { op: 'select', index, ...this._wsOpt() });
194
196
  if (result?.selected)
195
197
  this._tabId = result.selected;
196
198
  }
197
199
  async networkRequests(includeStatic = false) {
198
200
  const code = networkRequestsJs(includeStatic);
199
- const result = await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
201
+ const result = await sendCommand('exec', { code, ...this._cmdOpts() });
200
202
  return Array.isArray(result) ? result : [];
201
203
  }
202
204
  /**
@@ -216,30 +218,25 @@ export class Page {
216
218
  */
217
219
  async screenshot(options = {}) {
218
220
  const base64 = await sendCommand('screenshot', {
219
- ...this._workspaceOpt(),
221
+ ...this._cmdOpts(),
220
222
  format: options.format,
221
223
  quality: options.quality,
222
224
  fullPage: options.fullPage,
223
- ...this._tabOpt(),
224
225
  });
225
226
  if (options.path) {
226
- const fs = await import('node:fs');
227
- const path = await import('node:path');
228
- const dir = path.dirname(options.path);
229
- await fs.promises.mkdir(dir, { recursive: true });
230
- await fs.promises.writeFile(options.path, Buffer.from(base64, 'base64'));
227
+ await saveBase64ToFile(base64, options.path);
231
228
  }
232
229
  return base64;
233
230
  }
234
231
  async scroll(direction = 'down', amount = 500) {
235
232
  const code = scrollJs(direction, amount);
236
- await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
233
+ await sendCommand('exec', { code, ...this._cmdOpts() });
237
234
  }
238
235
  async autoScroll(options = {}) {
239
236
  const times = options.times ?? 3;
240
237
  const delayMs = options.delayMs ?? 2000;
241
238
  const code = autoScrollJs(times, delayMs);
242
- await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
239
+ await sendCommand('exec', { code, ...this._cmdOpts() });
243
240
  }
244
241
  async installInterceptor(pattern) {
245
242
  const { generateInterceptorJs } = await import('../interceptor.js');
@@ -8,8 +8,6 @@
8
8
  *
9
9
  * Inspired by puppeteer-extra-plugin-stealth.
10
10
  */
11
- /** Guard flag set on `window` to prevent double-injection. */
12
- export declare const STEALTH_GUARD = "__opencli_stealth_applied";
13
11
  /**
14
12
  * Return a self-contained JS string that, when evaluated in a page context,
15
13
  * applies all stealth patches. Safe to call multiple times — the guard flag
@@ -8,8 +8,6 @@
8
8
  *
9
9
  * Inspired by puppeteer-extra-plugin-stealth.
10
10
  */
11
- /** Guard flag set on `window` to prevent double-injection. */
12
- export const STEALTH_GUARD = '__opencli_stealth_applied';
13
11
  /**
14
12
  * Return a self-contained JS string that, when evaluated in a page context,
15
13
  * applies all stealth patches. Safe to call multiple times — the guard flag
@@ -18,16 +16,25 @@ export const STEALTH_GUARD = '__opencli_stealth_applied';
18
16
  export function generateStealthJs() {
19
17
  return `
20
18
  (() => {
21
- // Guard: skip if already applied
22
- if (window.${STEALTH_GUARD}) return 'skipped';
23
- // Use defineProperty so the guard flag is non-enumerable (not a detection vector).
24
- Object.defineProperty(window, '${STEALTH_GUARD}', { value: true, configurable: true });
19
+ // Guard: prevent double-injection across separate CDP evaluations.
20
+ // We cannot use a closure variable (each eval is a fresh scope), and
21
+ // window properties / Symbols are discoverable by anti-bot scripts.
22
+ // Instead, stash the flag in a non-enumerable getter on a built-in
23
+ // prototype that fingerprinters are unlikely to scan.
24
+ const _gProto = EventTarget.prototype;
25
+ const _gKey = '__lsn'; // looks like an internal listener cache
26
+ if (_gProto[_gKey]) return 'skipped';
27
+ try {
28
+ Object.defineProperty(_gProto, _gKey, { value: true, enumerable: false, configurable: true });
29
+ } catch {}
25
30
 
26
- // 1. navigator.webdriver → undefined
31
+ // 1. navigator.webdriver → false
27
32
  // Most common check; Playwright/Puppeteer/CDP set this to true.
33
+ // Real Chrome returns false (not undefined) — returning undefined is
34
+ // itself a detection signal for advanced fingerprinters.
28
35
  try {
29
36
  Object.defineProperty(navigator, 'webdriver', {
30
- get: () => undefined,
37
+ get: () => false,
31
38
  configurable: true,
32
39
  });
33
40
  } catch {}
@@ -117,9 +124,17 @@ export function generateStealthJs() {
117
124
  // We override the stack property getter on Error.prototype to filter them.
118
125
  // Note: Error.prepareStackTrace is V8/Node-only and not available in
119
126
  // browser page context, so we use a property descriptor approach instead.
127
+ // We use generic protocol patterns instead of product-specific names to
128
+ // also catch our own injected code frames without leaking identifiers.
120
129
  try {
121
130
  const _origDescriptor = Object.getOwnPropertyDescriptor(Error.prototype, 'stack');
122
- const _cdpPatterns = ['puppeteer_evaluation_script', 'pptr:', 'debugger://', '__opencli'];
131
+ const _cdpPatterns = [
132
+ 'puppeteer_evaluation_script',
133
+ 'pptr:',
134
+ 'debugger://',
135
+ '__playwright',
136
+ '__puppeteer',
137
+ ];
123
138
  if (_origDescriptor && _origDescriptor.get) {
124
139
  Object.defineProperty(Error.prototype, 'stack', {
125
140
  get: function () {
@@ -1,6 +1,5 @@
1
1
  import { describe, it, expect, vi } from 'vitest';
2
2
  import { BrowserBridge, __test__, generateStealthJs } from './browser/index.js';
3
- import { STEALTH_GUARD } from './browser/stealth.js';
4
3
  import * as daemonClient from './browser/daemon-client.js';
5
4
  describe('browser helpers', () => {
6
5
  it('extracts tab entries from string snapshots', () => {
@@ -136,7 +135,8 @@ describe('stealth anti-detection', () => {
136
135
  });
137
136
  it('includes guard flag to prevent double-injection', () => {
138
137
  const js = generateStealthJs();
139
- expect(js).toContain(STEALTH_GUARD);
138
+ // Guard uses a non-enumerable property on a built-in prototype
139
+ expect(js).toContain("EventTarget.prototype");
140
140
  // Guard should check early and return 'skipped'
141
141
  expect(js).toContain("return 'skipped'");
142
142
  // Normal path returns 'applied'
@@ -16,9 +16,7 @@ import { getErrorMessage } from './errors.js';
16
16
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
17
  const CLIS_DIR = path.resolve(__dirname, 'clis');
18
18
  const OUTPUT = path.resolve(__dirname, '..', 'dist', 'cli-manifest.json');
19
- function isRecord(value) {
20
- return typeof value === 'object' && value !== null && !Array.isArray(value);
21
- }
19
+ import { isRecord } from './utils.js';
22
20
  function extractBalancedBlock(source, startIndex, openChar, closeChar) {
23
21
  let depth = 0;
24
22
  let quote = null;
@@ -112,7 +110,9 @@ export function parseTsArgsBlock(argsBlock) {
112
110
  help: helpMatch?.[1] ?? '',
113
111
  choices: parseInlineChoices(body),
114
112
  });
115
- cursor = objectStart + body.length + 2;
113
+ cursor = objectStart + body.length;
114
+ if (cursor <= objectStart)
115
+ break; // safety: prevent infinite loop
116
116
  }
117
117
  return args;
118
118
  }
@@ -208,10 +208,16 @@ export function scanTs(filePath, site) {
208
208
  if (argsBlock) {
209
209
  entry.args = parseTsArgsBlock(argsBlock);
210
210
  }
211
- // Extract navigateBefore: false
212
- const navMatch = src.match(/navigateBefore\s*:\s*(true|false)/);
213
- if (navMatch)
214
- entry.navigateBefore = navMatch[1] === 'true' ? true : false;
211
+ // Extract navigateBefore: false / true / 'https://...'
212
+ const navBoolMatch = src.match(/navigateBefore\s*:\s*(true|false)/);
213
+ if (navBoolMatch) {
214
+ entry.navigateBefore = navBoolMatch[1] === 'true';
215
+ }
216
+ else {
217
+ const navStringMatch = src.match(/navigateBefore\s*:\s*['"`]([^'"`]+)['"`]/);
218
+ if (navStringMatch)
219
+ entry.navigateBefore = navStringMatch[1];
220
+ }
215
221
  return entry;
216
222
  }
217
223
  catch (err) {
@@ -226,7 +232,7 @@ export function scanTs(filePath, site) {
226
232
  */
227
233
  export function shouldReplaceManifestEntry(current, next) {
228
234
  if (current.type === next.type)
229
- return true;
235
+ return false;
230
236
  return current.type === 'yaml' && next.type === 'ts';
231
237
  }
232
238
  export function buildManifest() {
@@ -113,4 +113,16 @@ describe('manifest helper rules', () => {
113
113
  fs.writeFileSync(file, `export function helper() { return 'noop'; }`);
114
114
  expect(scanTs(file, 'demo')).toBeNull();
115
115
  });
116
+ it('keeps literal domain and navigateBefore for TS adapters', () => {
117
+ const file = path.join(process.cwd(), 'src', 'clis', 'xueqiu', 'fund-holdings.ts');
118
+ const entry = scanTs(file, 'xueqiu');
119
+ expect(entry).toMatchObject({
120
+ site: 'xueqiu',
121
+ name: 'fund-holdings',
122
+ domain: 'danjuanfunds.com',
123
+ navigateBefore: 'https://danjuanfunds.com/my-money',
124
+ type: 'ts',
125
+ modulePath: 'xueqiu/fund-holdings.js',
126
+ });
127
+ });
116
128
  });
package/dist/cascade.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * automatically finds the minimum-privilege strategy that works.
11
11
  */
12
12
  import { Strategy } from './registry.js';
13
+ import { getErrorMessage } from './errors.js';
13
14
  /** Strategy cascade order (simplest → most complex) */
14
15
  const CASCADE_ORDER = [
15
16
  Strategy.PUBLIC,
@@ -98,7 +99,7 @@ export async function probeEndpoint(page, url, strategy, _opts = {}) {
98
99
  }
99
100
  catch (err) {
100
101
  result.success = false;
101
- result.error = err.message ?? String(err);
102
+ result.error = getErrorMessage(err);
102
103
  }
103
104
  return result;
104
105
  }
@@ -107,9 +108,10 @@ export async function probeEndpoint(page, url, strategy, _opts = {}) {
107
108
  * Returns the simplest working strategy.
108
109
  */
109
110
  export async function cascadeProbe(page, url, opts = {}) {
110
- const maxIdx = opts.maxStrategy
111
+ const rawIdx = opts.maxStrategy
111
112
  ? CASCADE_ORDER.indexOf(opts.maxStrategy)
112
113
  : CASCADE_ORDER.indexOf(Strategy.HEADER); // Don't auto-try INTERCEPT/UI
114
+ const maxIdx = rawIdx === -1 ? CASCADE_ORDER.indexOf(Strategy.HEADER) : rawIdx;
113
115
  const probes = [];
114
116
  for (let i = 0; i <= Math.min(maxIdx, CASCADE_ORDER.length - 1); i++) {
115
117
  const strategy = CASCADE_ORDER[i];