@jackwener/opencli 1.3.3 → 1.4.1

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 (680) hide show
  1. package/.github/actions/setup-chrome/action.yml +5 -4
  2. package/.github/pull_request_template.md +3 -1
  3. package/.github/workflows/build-extension.yml +7 -1
  4. package/.github/workflows/ci.yml +46 -6
  5. package/.github/workflows/docs.yml +1 -1
  6. package/.github/workflows/e2e-headed.yml +36 -3
  7. package/.github/workflows/release.yml +1 -1
  8. package/.github/workflows/security.yml +0 -3
  9. package/CHANGELOG.md +78 -0
  10. package/CONTRIBUTING.md +6 -3
  11. package/PRIVACY.md +57 -0
  12. package/README.md +31 -4
  13. package/README.zh-CN.md +31 -4
  14. package/SKILL.md +107 -2
  15. package/TESTING.md +1 -0
  16. package/chatwise-opencli.ps1 +82 -0
  17. package/dist/analysis.d.ts +38 -0
  18. package/dist/analysis.js +166 -0
  19. package/dist/browser/cdp.d.ts +0 -4
  20. package/dist/browser/cdp.js +53 -41
  21. package/dist/browser/cdp.test.d.ts +1 -0
  22. package/dist/browser/cdp.test.js +52 -0
  23. package/dist/browser/dom-snapshot.d.ts +2 -2
  24. package/dist/browser/dom-snapshot.js +54 -1
  25. package/dist/browser/dom-snapshot.test.js +36 -0
  26. package/dist/browser/index.d.ts +2 -2
  27. package/dist/browser/index.js +1 -1
  28. package/dist/browser/mcp.d.ts +0 -2
  29. package/dist/browser/mcp.js +2 -3
  30. package/dist/browser/page.d.ts +4 -3
  31. package/dist/browser/page.js +34 -37
  32. package/dist/browser/stealth.d.ts +0 -2
  33. package/dist/browser/stealth.js +24 -9
  34. package/dist/browser.test.js +2 -2
  35. package/dist/build-manifest.js +15 -9
  36. package/dist/build-manifest.test.js +12 -0
  37. package/dist/cascade.js +4 -2
  38. package/dist/cli-manifest.json +1325 -256
  39. package/dist/cli.js +57 -29
  40. package/dist/clis/_shared/desktop-commands.d.ts +22 -0
  41. package/dist/clis/_shared/desktop-commands.js +108 -0
  42. package/dist/clis/antigravity/serve.js +5 -2
  43. package/dist/clis/apple-podcasts/search.js +2 -1
  44. package/dist/clis/arxiv/search.js +3 -3
  45. package/dist/clis/bbc/news.js +0 -1
  46. package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
  47. package/dist/clis/bilibili/dynamic.test.js +68 -0
  48. package/dist/clis/bilibili/favorite.js +4 -2
  49. package/dist/clis/bilibili/following.js +3 -2
  50. package/dist/clis/bilibili/subtitle.js +8 -7
  51. package/dist/clis/bilibili/utils.js +2 -2
  52. package/dist/clis/boss/batchgreet.js +1 -1
  53. package/dist/clis/boss/chatlist.js +1 -1
  54. package/dist/clis/boss/chatmsg.js +1 -1
  55. package/dist/clis/boss/detail.js +1 -1
  56. package/dist/clis/boss/exchange.js +1 -1
  57. package/dist/clis/boss/greet.js +1 -1
  58. package/dist/clis/boss/invite.js +1 -1
  59. package/dist/clis/boss/joblist.js +1 -1
  60. package/dist/clis/boss/mark.js +4 -3
  61. package/dist/clis/boss/recommend.js +1 -1
  62. package/dist/clis/boss/resume.js +1 -1
  63. package/dist/clis/boss/search.js +1 -1
  64. package/dist/clis/boss/send.js +5 -4
  65. package/dist/clis/boss/stats.js +1 -1
  66. package/dist/clis/chatgpt/ask.js +4 -0
  67. package/dist/clis/chatgpt/new.js +5 -1
  68. package/dist/clis/chatgpt/read.js +5 -1
  69. package/dist/clis/chatgpt/send.js +2 -1
  70. package/dist/clis/chatgpt/status.js +5 -1
  71. package/dist/clis/chatwise/ask.js +8 -2
  72. package/dist/clis/chatwise/export.js +2 -0
  73. package/dist/clis/chatwise/history.js +2 -0
  74. package/dist/clis/chatwise/model.js +8 -3
  75. package/dist/clis/chatwise/new.js +3 -18
  76. package/dist/clis/chatwise/read.js +2 -0
  77. package/dist/clis/chatwise/screenshot.js +3 -27
  78. package/dist/clis/chatwise/send.js +8 -2
  79. package/dist/clis/chatwise/shared.d.ts +2 -0
  80. package/dist/clis/chatwise/shared.js +6 -0
  81. package/dist/clis/chatwise/status.js +3 -22
  82. package/dist/clis/codex/ask.js +6 -2
  83. package/dist/clis/codex/dump.js +2 -25
  84. package/dist/clis/codex/new.js +2 -25
  85. package/dist/clis/codex/screenshot.js +2 -27
  86. package/dist/clis/codex/send.js +6 -4
  87. package/dist/clis/codex/status.js +2 -22
  88. package/dist/clis/ctrip/search.js +0 -1
  89. package/dist/clis/cursor/ask.js +2 -1
  90. package/dist/clis/cursor/composer.js +2 -1
  91. package/dist/clis/cursor/dump.js +2 -25
  92. package/dist/clis/cursor/new.js +2 -18
  93. package/dist/clis/cursor/read.js +2 -1
  94. package/dist/clis/cursor/screenshot.js +1 -30
  95. package/dist/clis/cursor/send.js +2 -1
  96. package/dist/clis/cursor/status.js +2 -21
  97. package/dist/clis/dictionary/examples.yaml +25 -0
  98. package/dist/clis/dictionary/search.yaml +27 -0
  99. package/dist/clis/dictionary/synonyms.yaml +25 -0
  100. package/dist/clis/douban/book-hot.js +1 -1
  101. package/dist/clis/douban/movie-hot.js +1 -1
  102. package/dist/clis/douban/search.js +1 -1
  103. package/dist/clis/douban/utils.d.ts +4 -1
  104. package/dist/clis/douban/utils.js +156 -1
  105. package/dist/clis/doubao/ask.js +1 -1
  106. package/dist/clis/doubao/new.js +1 -1
  107. package/dist/clis/doubao/read.js +1 -1
  108. package/dist/clis/doubao/send.js +1 -1
  109. package/dist/clis/doubao/status.js +1 -1
  110. package/dist/clis/doubao-app/ask.js +1 -1
  111. package/dist/clis/doubao-app/new.js +1 -1
  112. package/dist/clis/doubao-app/read.js +1 -1
  113. package/dist/clis/doubao-app/send.js +1 -1
  114. package/dist/clis/douyin/_shared/browser-fetch.d.ts +10 -0
  115. package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
  116. package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
  117. package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
  118. package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
  119. package/dist/clis/douyin/_shared/creation-id.js +5 -0
  120. package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
  121. package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
  122. package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
  123. package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
  124. package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
  125. package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
  126. package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
  127. package/dist/clis/douyin/_shared/sts2.js +15 -0
  128. package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
  129. package/dist/clis/douyin/_shared/text-extra.js +15 -0
  130. package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
  131. package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
  132. package/dist/clis/douyin/_shared/timing.d.ts +2 -0
  133. package/dist/clis/douyin/_shared/timing.js +22 -0
  134. package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
  135. package/dist/clis/douyin/_shared/timing.test.js +28 -0
  136. package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
  137. package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
  138. package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
  139. package/dist/clis/douyin/_shared/tos-upload.js +295 -0
  140. package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
  141. package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
  142. package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
  143. package/dist/clis/douyin/_shared/transcode.js +45 -0
  144. package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
  145. package/dist/clis/douyin/_shared/transcode.test.js +93 -0
  146. package/dist/clis/douyin/_shared/types.d.ts +26 -0
  147. package/dist/clis/douyin/_shared/types.js +1 -0
  148. package/dist/clis/douyin/activities.d.ts +1 -0
  149. package/dist/clis/douyin/activities.js +20 -0
  150. package/dist/clis/douyin/activities.test.d.ts +1 -0
  151. package/dist/clis/douyin/activities.test.js +22 -0
  152. package/dist/clis/douyin/collections.d.ts +1 -0
  153. package/dist/clis/douyin/collections.js +22 -0
  154. package/dist/clis/douyin/collections.test.d.ts +1 -0
  155. package/dist/clis/douyin/collections.test.js +23 -0
  156. package/dist/clis/douyin/delete.d.ts +1 -0
  157. package/dist/clis/douyin/delete.js +18 -0
  158. package/dist/clis/douyin/delete.test.d.ts +1 -0
  159. package/dist/clis/douyin/delete.test.js +11 -0
  160. package/dist/clis/douyin/draft.d.ts +14 -0
  161. package/dist/clis/douyin/draft.js +237 -0
  162. package/dist/clis/douyin/draft.test.d.ts +1 -0
  163. package/dist/clis/douyin/draft.test.js +11 -0
  164. package/dist/clis/douyin/drafts.d.ts +1 -0
  165. package/dist/clis/douyin/drafts.js +23 -0
  166. package/dist/clis/douyin/drafts.test.d.ts +1 -0
  167. package/dist/clis/douyin/drafts.test.js +11 -0
  168. package/dist/clis/douyin/hashtag.d.ts +1 -0
  169. package/dist/clis/douyin/hashtag.js +45 -0
  170. package/dist/clis/douyin/hashtag.test.d.ts +1 -0
  171. package/dist/clis/douyin/hashtag.test.js +25 -0
  172. package/dist/clis/douyin/location.d.ts +1 -0
  173. package/dist/clis/douyin/location.js +24 -0
  174. package/dist/clis/douyin/location.test.d.ts +1 -0
  175. package/dist/clis/douyin/location.test.js +23 -0
  176. package/dist/clis/douyin/profile.d.ts +1 -0
  177. package/dist/clis/douyin/profile.js +28 -0
  178. package/dist/clis/douyin/profile.test.d.ts +1 -0
  179. package/dist/clis/douyin/profile.test.js +11 -0
  180. package/dist/clis/douyin/publish.d.ts +14 -0
  181. package/dist/clis/douyin/publish.js +288 -0
  182. package/dist/clis/douyin/publish.test.d.ts +1 -0
  183. package/dist/clis/douyin/publish.test.js +38 -0
  184. package/dist/clis/douyin/stats.d.ts +1 -0
  185. package/dist/clis/douyin/stats.js +27 -0
  186. package/dist/clis/douyin/stats.test.d.ts +1 -0
  187. package/dist/clis/douyin/stats.test.js +22 -0
  188. package/dist/clis/douyin/update.d.ts +1 -0
  189. package/dist/clis/douyin/update.js +31 -0
  190. package/dist/clis/douyin/update.test.d.ts +1 -0
  191. package/dist/clis/douyin/update.test.js +11 -0
  192. package/dist/clis/douyin/videos.d.ts +1 -0
  193. package/dist/clis/douyin/videos.js +34 -0
  194. package/dist/clis/douyin/videos.test.d.ts +1 -0
  195. package/dist/clis/douyin/videos.test.js +11 -0
  196. package/dist/clis/grok/ask.d.ts +4 -0
  197. package/dist/clis/grok/ask.js +28 -10
  198. package/dist/clis/grok/ask.test.js +18 -0
  199. package/dist/clis/hackernews/search.yaml +1 -1
  200. package/dist/clis/instagram/search.yaml +2 -1
  201. package/dist/clis/jd/item.d.ts +1 -0
  202. package/dist/clis/jd/item.js +96 -0
  203. package/dist/clis/jd/item.test.d.ts +1 -0
  204. package/dist/clis/jd/item.test.js +28 -0
  205. package/dist/clis/jike/feed.js +1 -1
  206. package/dist/clis/jike/search.js +1 -1
  207. package/dist/clis/linkedin/search.js +5 -4
  208. package/dist/clis/linkedin/timeline.d.ts +21 -0
  209. package/dist/clis/linkedin/timeline.js +503 -0
  210. package/dist/clis/linkedin/timeline.test.d.ts +1 -0
  211. package/dist/clis/linkedin/timeline.test.js +81 -0
  212. package/dist/clis/linux-do/search.yaml +3 -1
  213. package/dist/clis/medium/feed.js +1 -1
  214. package/dist/clis/medium/search.js +2 -2
  215. package/dist/clis/medium/user.js +1 -1
  216. package/dist/clis/medium/{shared.js → utils.js} +2 -1
  217. package/dist/clis/pixiv/detail.yaml +49 -0
  218. package/dist/clis/pixiv/download.d.ts +7 -0
  219. package/dist/clis/pixiv/download.js +78 -0
  220. package/dist/clis/pixiv/download.test.d.ts +1 -0
  221. package/dist/clis/pixiv/download.test.js +87 -0
  222. package/dist/clis/pixiv/illusts.d.ts +8 -0
  223. package/dist/clis/pixiv/illusts.js +65 -0
  224. package/dist/clis/pixiv/illusts.test.d.ts +1 -0
  225. package/dist/clis/pixiv/illusts.test.js +99 -0
  226. package/dist/clis/pixiv/ranking.yaml +53 -0
  227. package/dist/clis/pixiv/search.d.ts +6 -0
  228. package/dist/clis/pixiv/search.js +43 -0
  229. package/dist/clis/pixiv/search.test.d.ts +1 -0
  230. package/dist/clis/pixiv/search.test.js +83 -0
  231. package/dist/clis/pixiv/test-utils.d.ts +12 -0
  232. package/dist/clis/pixiv/test-utils.js +23 -0
  233. package/dist/clis/pixiv/user.yaml +46 -0
  234. package/dist/clis/pixiv/utils.d.ts +27 -0
  235. package/dist/clis/pixiv/utils.js +49 -0
  236. package/dist/clis/reddit/comment.js +2 -1
  237. package/dist/clis/reddit/read.js +4 -3
  238. package/dist/clis/reddit/read.test.d.ts +1 -0
  239. package/dist/clis/reddit/read.test.js +28 -0
  240. package/dist/clis/reddit/save.js +2 -1
  241. package/dist/clis/reddit/saved.js +7 -3
  242. package/dist/clis/reddit/subscribe.js +2 -1
  243. package/dist/clis/reddit/upvote.js +2 -1
  244. package/dist/clis/reddit/upvoted.js +7 -3
  245. package/dist/clis/reuters/search.js +0 -1
  246. package/dist/clis/sinablog/article.js +1 -1
  247. package/dist/clis/sinablog/hot.js +1 -1
  248. package/dist/clis/sinablog/user.js +1 -1
  249. package/dist/clis/substack/feed.js +1 -1
  250. package/dist/clis/substack/publication.js +1 -1
  251. package/dist/clis/substack/search.js +3 -2
  252. package/dist/clis/substack/{shared.js → utils.js} +3 -2
  253. package/dist/clis/tiktok/search.yaml +2 -1
  254. package/dist/clis/twitter/accept.js +2 -1
  255. package/dist/clis/twitter/article.js +4 -1
  256. package/dist/clis/twitter/block.js +2 -1
  257. package/dist/clis/twitter/bookmark.js +2 -1
  258. package/dist/clis/twitter/bookmarks.js +3 -2
  259. package/dist/clis/twitter/delete.js +2 -1
  260. package/dist/clis/twitter/follow.js +2 -1
  261. package/dist/clis/twitter/followers.js +3 -2
  262. package/dist/clis/twitter/following.js +3 -2
  263. package/dist/clis/twitter/hide-reply.js +2 -1
  264. package/dist/clis/twitter/like.js +2 -1
  265. package/dist/clis/twitter/notifications.js +2 -1
  266. package/dist/clis/twitter/post.js +2 -1
  267. package/dist/clis/twitter/profile.js +5 -2
  268. package/dist/clis/twitter/reply-dm.js +2 -1
  269. package/dist/clis/twitter/reply.js +2 -1
  270. package/dist/clis/twitter/search.js +32 -13
  271. package/dist/clis/twitter/search.test.d.ts +1 -0
  272. package/dist/clis/twitter/search.test.js +156 -0
  273. package/dist/clis/twitter/thread.js +2 -2
  274. package/dist/clis/twitter/timeline.js +3 -2
  275. package/dist/clis/twitter/trending.js +3 -2
  276. package/dist/clis/twitter/unblock.js +2 -1
  277. package/dist/clis/twitter/unbookmark.js +2 -1
  278. package/dist/clis/twitter/unfollow.js +2 -1
  279. package/dist/clis/v2ex/daily.js +3 -2
  280. package/dist/clis/v2ex/me.js +3 -2
  281. package/dist/clis/v2ex/notifications.js +4 -4
  282. package/dist/clis/web/read.d.ts +16 -0
  283. package/dist/clis/web/read.js +202 -0
  284. package/dist/clis/weibo/comments.d.ts +1 -0
  285. package/dist/clis/weibo/comments.js +53 -0
  286. package/dist/clis/weibo/feed.d.ts +1 -0
  287. package/dist/clis/weibo/feed.js +56 -0
  288. package/dist/clis/weibo/hot.js +0 -1
  289. package/dist/clis/weibo/me.d.ts +1 -0
  290. package/dist/clis/weibo/me.js +76 -0
  291. package/dist/clis/weibo/post.d.ts +1 -0
  292. package/dist/clis/weibo/post.js +75 -0
  293. package/dist/clis/weibo/user.d.ts +1 -0
  294. package/dist/clis/weibo/user.js +63 -0
  295. package/dist/clis/weibo/utils.d.ts +6 -0
  296. package/dist/clis/weibo/utils.js +30 -0
  297. package/dist/clis/weread/search.js +3 -2
  298. package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
  299. package/dist/clis/xueqiu/danjuan-utils.js +126 -0
  300. package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
  301. package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
  302. package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
  303. package/dist/clis/xueqiu/fund-holdings.js +28 -0
  304. package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
  305. package/dist/clis/xueqiu/fund-snapshot.js +25 -0
  306. package/dist/clis/xueqiu/search.yaml +2 -1
  307. package/dist/clis/yahoo-finance/quote.js +0 -1
  308. package/dist/clis/youtube/channel.d.ts +1 -0
  309. package/dist/clis/youtube/channel.js +150 -0
  310. package/dist/clis/youtube/comments.d.ts +1 -0
  311. package/dist/clis/youtube/comments.js +95 -0
  312. package/dist/clis/youtube/search.js +0 -1
  313. package/dist/clis/youtube/transcript.js +5 -4
  314. package/dist/clis/youtube/video.js +3 -2
  315. package/dist/clis/zhihu/search.yaml +2 -1
  316. package/dist/daemon.js +7 -3
  317. package/dist/discovery.js +11 -10
  318. package/dist/doctor.js +2 -1
  319. package/dist/download/index.d.ts +4 -12
  320. package/dist/download/index.js +33 -12
  321. package/dist/download/index.test.js +79 -2
  322. package/dist/download/media-download.js +4 -2
  323. package/dist/engine.test.js +76 -4
  324. package/dist/execution.d.ts +1 -9
  325. package/dist/execution.js +56 -46
  326. package/dist/explore.js +12 -111
  327. package/dist/external-clis.yaml +0 -25
  328. package/dist/external.js +7 -5
  329. package/dist/external.test.js +4 -0
  330. package/dist/generate.d.ts +0 -9
  331. package/dist/generate.js +4 -20
  332. package/dist/hooks.d.ts +46 -0
  333. package/dist/hooks.js +56 -0
  334. package/dist/hooks.test.d.ts +4 -0
  335. package/dist/hooks.test.js +92 -0
  336. package/dist/interceptor.js +70 -23
  337. package/dist/main.js +2 -0
  338. package/dist/output.js +12 -6
  339. package/dist/pipeline/executor.js +1 -1
  340. package/dist/pipeline/steps/browser.js +1 -3
  341. package/dist/pipeline/steps/download.js +42 -26
  342. package/dist/pipeline/steps/download.test.d.ts +1 -0
  343. package/dist/pipeline/steps/download.test.js +101 -0
  344. package/dist/pipeline/steps/fetch.js +40 -22
  345. package/dist/pipeline/steps/fetch.test.d.ts +1 -0
  346. package/dist/pipeline/steps/fetch.test.js +123 -0
  347. package/dist/pipeline/steps/transform.js +2 -6
  348. package/dist/pipeline/template.js +66 -52
  349. package/dist/pipeline/template.test.js +28 -0
  350. package/dist/pipeline/transform.test.js +18 -0
  351. package/dist/plugin.d.ts +40 -1
  352. package/dist/plugin.js +214 -17
  353. package/dist/plugin.test.d.ts +1 -1
  354. package/dist/plugin.test.js +219 -3
  355. package/dist/record.js +6 -98
  356. package/dist/registry-api.d.ts +2 -0
  357. package/dist/registry-api.js +1 -0
  358. package/dist/registry.d.ts +5 -2
  359. package/dist/registry.js +1 -2
  360. package/dist/runtime.d.ts +0 -1
  361. package/dist/runtime.js +14 -4
  362. package/dist/snapshotFormatter.d.ts +7 -14
  363. package/dist/snapshotFormatter.js +38 -78
  364. package/dist/utils.d.ts +9 -0
  365. package/dist/utils.js +29 -0
  366. package/dist/validate.js +3 -5
  367. package/dist/weread-search-regression.test.d.ts +1 -0
  368. package/dist/weread-search-regression.test.js +39 -0
  369. package/dist/yaml-schema.d.ts +26 -0
  370. package/dist/yaml-schema.js +5 -0
  371. package/docs/.vitepress/config.mts +16 -0
  372. package/docs/adapters/browser/dictionary.md +27 -0
  373. package/docs/adapters/browser/douyin.md +75 -0
  374. package/docs/adapters/browser/jd.md +27 -0
  375. package/docs/adapters/browser/linkedin.md +6 -0
  376. package/docs/adapters/browser/pixiv.md +92 -0
  377. package/docs/adapters/browser/twitter.md +6 -0
  378. package/docs/adapters/browser/web.md +30 -0
  379. package/docs/adapters/browser/xueqiu.md +27 -9
  380. package/docs/adapters/index.md +9 -2
  381. package/docs/comparison.md +125 -0
  382. package/docs/developer/contributing.md +21 -2
  383. package/docs/developer/testing.md +14 -8
  384. package/docs/developer/ts-adapter.md +18 -0
  385. package/docs/developer/yaml-adapter.md +16 -0
  386. package/docs/guide/plugins.md +10 -0
  387. package/docs/zh/guide/plugins.md +10 -0
  388. package/extension/dist/background.js +100 -35
  389. package/extension/manifest.json +6 -2
  390. package/extension/package.json +1 -1
  391. package/extension/popup.html +84 -0
  392. package/extension/popup.js +25 -0
  393. package/extension/src/background.test.ts +46 -1
  394. package/extension/src/background.ts +128 -34
  395. package/extension/src/cdp.ts +9 -9
  396. package/package.json +3 -2
  397. package/scripts/check-doc-coverage.sh +2 -0
  398. package/src/analysis.ts +170 -0
  399. package/src/browser/cdp.test.ts +66 -0
  400. package/src/browser/cdp.ts +59 -44
  401. package/src/browser/dom-snapshot.test.ts +42 -0
  402. package/src/browser/dom-snapshot.ts +56 -3
  403. package/src/browser/index.ts +2 -2
  404. package/src/browser/mcp.ts +2 -4
  405. package/src/browser/page.ts +34 -37
  406. package/src/browser/stealth.ts +24 -10
  407. package/src/browser.test.ts +2 -2
  408. package/src/build-manifest.test.ts +14 -0
  409. package/src/build-manifest.ts +13 -31
  410. package/src/cascade.ts +5 -3
  411. package/src/cli.ts +66 -34
  412. package/src/clis/_shared/desktop-commands.ts +121 -0
  413. package/src/clis/antigravity/serve.ts +6 -3
  414. package/src/clis/apple-podcasts/search.ts +2 -1
  415. package/src/clis/arxiv/search.ts +3 -3
  416. package/src/clis/bbc/news.ts +0 -1
  417. package/src/clis/bilibili/dynamic.test.ts +79 -0
  418. package/src/clis/bilibili/favorite.ts +5 -2
  419. package/src/clis/bilibili/following.ts +3 -2
  420. package/src/clis/bilibili/subtitle.ts +8 -7
  421. package/src/clis/bilibili/utils.ts +2 -2
  422. package/src/clis/boss/batchgreet.ts +1 -1
  423. package/src/clis/boss/chatlist.ts +1 -1
  424. package/src/clis/boss/chatmsg.ts +1 -1
  425. package/src/clis/boss/detail.ts +1 -1
  426. package/src/clis/boss/exchange.ts +1 -1
  427. package/src/clis/boss/greet.ts +1 -1
  428. package/src/clis/boss/invite.ts +1 -1
  429. package/src/clis/boss/joblist.ts +1 -1
  430. package/src/clis/boss/mark.ts +4 -3
  431. package/src/clis/boss/recommend.ts +1 -1
  432. package/src/clis/boss/resume.ts +1 -1
  433. package/src/clis/boss/search.ts +1 -1
  434. package/src/clis/boss/send.ts +5 -4
  435. package/src/clis/boss/stats.ts +1 -1
  436. package/src/clis/chatgpt/ask.ts +5 -0
  437. package/src/clis/chatgpt/new.ts +7 -2
  438. package/src/clis/chatgpt/read.ts +7 -2
  439. package/src/clis/chatgpt/send.ts +3 -2
  440. package/src/clis/chatgpt/status.ts +6 -1
  441. package/src/clis/chatwise/ask.ts +7 -2
  442. package/src/clis/chatwise/export.ts +2 -0
  443. package/src/clis/chatwise/history.ts +2 -0
  444. package/src/clis/chatwise/model.ts +7 -3
  445. package/src/clis/chatwise/new.ts +3 -20
  446. package/src/clis/chatwise/read.ts +2 -0
  447. package/src/clis/chatwise/screenshot.ts +3 -32
  448. package/src/clis/chatwise/send.ts +7 -2
  449. package/src/clis/chatwise/shared.ts +8 -0
  450. package/src/clis/chatwise/status.ts +3 -24
  451. package/src/clis/codex/ask.ts +5 -2
  452. package/src/clis/codex/dump.ts +2 -27
  453. package/src/clis/codex/new.ts +2 -28
  454. package/src/clis/codex/screenshot.ts +2 -32
  455. package/src/clis/codex/send.ts +5 -4
  456. package/src/clis/codex/status.ts +2 -24
  457. package/src/clis/ctrip/search.ts +0 -1
  458. package/src/clis/cursor/ask.ts +2 -1
  459. package/src/clis/cursor/composer.ts +2 -1
  460. package/src/clis/cursor/dump.ts +2 -27
  461. package/src/clis/cursor/new.ts +2 -20
  462. package/src/clis/cursor/read.ts +2 -1
  463. package/src/clis/cursor/screenshot.ts +1 -36
  464. package/src/clis/cursor/send.ts +2 -1
  465. package/src/clis/cursor/status.ts +2 -22
  466. package/src/clis/dictionary/examples.yaml +25 -0
  467. package/src/clis/dictionary/search.yaml +27 -0
  468. package/src/clis/dictionary/synonyms.yaml +25 -0
  469. package/src/clis/douban/book-hot.ts +1 -1
  470. package/src/clis/douban/movie-hot.ts +1 -1
  471. package/src/clis/douban/search.ts +1 -1
  472. package/src/clis/douban/utils.ts +165 -1
  473. package/src/clis/doubao/ask.ts +1 -1
  474. package/src/clis/doubao/new.ts +1 -1
  475. package/src/clis/doubao/read.ts +1 -1
  476. package/src/clis/doubao/send.ts +1 -1
  477. package/src/clis/doubao/status.ts +1 -1
  478. package/src/clis/doubao-app/ask.ts +1 -1
  479. package/src/clis/doubao-app/new.ts +1 -1
  480. package/src/clis/doubao-app/read.ts +1 -1
  481. package/src/clis/doubao-app/send.ts +1 -1
  482. package/src/clis/douyin/_shared/browser-fetch.test.ts +38 -0
  483. package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
  484. package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
  485. package/src/clis/douyin/_shared/creation-id.ts +8 -0
  486. package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
  487. package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
  488. package/src/clis/douyin/_shared/sts2.ts +20 -0
  489. package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
  490. package/src/clis/douyin/_shared/text-extra.ts +33 -0
  491. package/src/clis/douyin/_shared/timing.test.ts +38 -0
  492. package/src/clis/douyin/_shared/timing.ts +22 -0
  493. package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
  494. package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
  495. package/src/clis/douyin/_shared/tos-upload.ts +444 -0
  496. package/src/clis/douyin/_shared/transcode.test.ts +117 -0
  497. package/src/clis/douyin/_shared/transcode.ts +78 -0
  498. package/src/clis/douyin/_shared/types.ts +29 -0
  499. package/src/clis/douyin/activities.test.ts +25 -0
  500. package/src/clis/douyin/activities.ts +23 -0
  501. package/src/clis/douyin/collections.test.ts +26 -0
  502. package/src/clis/douyin/collections.ts +25 -0
  503. package/src/clis/douyin/delete.test.ts +12 -0
  504. package/src/clis/douyin/delete.ts +20 -0
  505. package/src/clis/douyin/draft.test.ts +12 -0
  506. package/src/clis/douyin/draft.ts +282 -0
  507. package/src/clis/douyin/drafts.test.ts +12 -0
  508. package/src/clis/douyin/drafts.ts +27 -0
  509. package/src/clis/douyin/hashtag.test.ts +28 -0
  510. package/src/clis/douyin/hashtag.ts +56 -0
  511. package/src/clis/douyin/location.test.ts +26 -0
  512. package/src/clis/douyin/location.ts +27 -0
  513. package/src/clis/douyin/profile.test.ts +12 -0
  514. package/src/clis/douyin/profile.ts +37 -0
  515. package/src/clis/douyin/publish.test.ts +45 -0
  516. package/src/clis/douyin/publish.ts +340 -0
  517. package/src/clis/douyin/stats.test.ts +25 -0
  518. package/src/clis/douyin/stats.ts +30 -0
  519. package/src/clis/douyin/update.test.ts +12 -0
  520. package/src/clis/douyin/update.ts +43 -0
  521. package/src/clis/douyin/videos.test.ts +12 -0
  522. package/src/clis/douyin/videos.ts +49 -0
  523. package/src/clis/grok/ask.test.ts +25 -0
  524. package/src/clis/grok/ask.ts +25 -12
  525. package/src/clis/hackernews/search.yaml +1 -1
  526. package/src/clis/instagram/search.yaml +2 -1
  527. package/src/clis/jd/item.test.ts +35 -0
  528. package/src/clis/jd/item.ts +101 -0
  529. package/src/clis/jike/feed.ts +1 -1
  530. package/src/clis/jike/search.ts +1 -1
  531. package/src/clis/linkedin/search.ts +5 -4
  532. package/src/clis/linkedin/timeline.test.ts +99 -0
  533. package/src/clis/linkedin/timeline.ts +532 -0
  534. package/src/clis/linux-do/search.yaml +3 -1
  535. package/src/clis/medium/feed.ts +1 -1
  536. package/src/clis/medium/search.ts +2 -2
  537. package/src/clis/medium/user.ts +1 -1
  538. package/src/clis/medium/{shared.ts → utils.ts} +2 -1
  539. package/src/clis/pixiv/detail.yaml +49 -0
  540. package/src/clis/pixiv/download.test.ts +114 -0
  541. package/src/clis/pixiv/download.ts +91 -0
  542. package/src/clis/pixiv/illusts.test.ts +115 -0
  543. package/src/clis/pixiv/illusts.ts +78 -0
  544. package/src/clis/pixiv/ranking.yaml +53 -0
  545. package/src/clis/pixiv/search.test.ts +97 -0
  546. package/src/clis/pixiv/search.ts +53 -0
  547. package/src/clis/pixiv/test-utils.ts +29 -0
  548. package/src/clis/pixiv/user.yaml +46 -0
  549. package/src/clis/pixiv/utils.ts +62 -0
  550. package/src/clis/reddit/comment.ts +2 -1
  551. package/src/clis/reddit/read.test.ts +34 -0
  552. package/src/clis/reddit/read.ts +4 -3
  553. package/src/clis/reddit/save.ts +2 -1
  554. package/src/clis/reddit/saved.ts +6 -2
  555. package/src/clis/reddit/subscribe.ts +2 -1
  556. package/src/clis/reddit/upvote.ts +2 -1
  557. package/src/clis/reddit/upvoted.ts +6 -2
  558. package/src/clis/reuters/search.ts +0 -1
  559. package/src/clis/sinablog/article.ts +1 -1
  560. package/src/clis/sinablog/hot.ts +1 -1
  561. package/src/clis/sinablog/user.ts +1 -1
  562. package/src/clis/substack/feed.ts +1 -1
  563. package/src/clis/substack/publication.ts +1 -1
  564. package/src/clis/substack/search.ts +3 -2
  565. package/src/clis/substack/{shared.ts → utils.ts} +3 -2
  566. package/src/clis/tiktok/search.yaml +2 -1
  567. package/src/clis/twitter/accept.ts +2 -1
  568. package/src/clis/twitter/article.ts +3 -1
  569. package/src/clis/twitter/block.ts +2 -1
  570. package/src/clis/twitter/bookmark.ts +2 -1
  571. package/src/clis/twitter/bookmarks.ts +3 -2
  572. package/src/clis/twitter/delete.ts +2 -1
  573. package/src/clis/twitter/follow.ts +2 -1
  574. package/src/clis/twitter/followers.ts +3 -2
  575. package/src/clis/twitter/following.ts +3 -2
  576. package/src/clis/twitter/hide-reply.ts +2 -1
  577. package/src/clis/twitter/like.ts +2 -1
  578. package/src/clis/twitter/notifications.ts +2 -1
  579. package/src/clis/twitter/post.ts +2 -1
  580. package/src/clis/twitter/profile.ts +4 -2
  581. package/src/clis/twitter/reply-dm.ts +2 -1
  582. package/src/clis/twitter/reply.ts +2 -1
  583. package/src/clis/twitter/search.test.ts +180 -0
  584. package/src/clis/twitter/search.ts +40 -14
  585. package/src/clis/twitter/thread.ts +2 -2
  586. package/src/clis/twitter/timeline.ts +3 -2
  587. package/src/clis/twitter/trending.ts +3 -2
  588. package/src/clis/twitter/unblock.ts +2 -1
  589. package/src/clis/twitter/unbookmark.ts +2 -1
  590. package/src/clis/twitter/unfollow.ts +2 -1
  591. package/src/clis/v2ex/daily.ts +3 -2
  592. package/src/clis/v2ex/me.ts +3 -2
  593. package/src/clis/v2ex/notifications.ts +3 -4
  594. package/src/clis/web/read.ts +210 -0
  595. package/src/clis/weibo/comments.ts +54 -0
  596. package/src/clis/weibo/feed.ts +57 -0
  597. package/src/clis/weibo/hot.ts +0 -1
  598. package/src/clis/weibo/me.ts +77 -0
  599. package/src/clis/weibo/post.ts +77 -0
  600. package/src/clis/weibo/user.ts +64 -0
  601. package/src/clis/weibo/utils.ts +32 -0
  602. package/src/clis/weread/search.ts +3 -2
  603. package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
  604. package/src/clis/xueqiu/danjuan-utils.ts +176 -0
  605. package/src/clis/xueqiu/fund-holdings.ts +32 -0
  606. package/src/clis/xueqiu/fund-snapshot.ts +27 -0
  607. package/src/clis/xueqiu/search.yaml +2 -1
  608. package/src/clis/yahoo-finance/quote.ts +0 -1
  609. package/src/clis/youtube/channel.ts +155 -0
  610. package/src/clis/youtube/comments.ts +97 -0
  611. package/src/clis/youtube/search.ts +0 -1
  612. package/src/clis/youtube/transcript.ts +5 -4
  613. package/src/clis/youtube/video.ts +3 -2
  614. package/src/clis/zhihu/search.yaml +2 -1
  615. package/src/daemon.ts +5 -4
  616. package/src/discovery.ts +12 -34
  617. package/src/doctor.ts +3 -2
  618. package/src/download/index.test.ts +93 -2
  619. package/src/download/index.ts +44 -23
  620. package/src/download/media-download.ts +5 -3
  621. package/src/engine.test.ts +84 -3
  622. package/src/execution.ts +62 -46
  623. package/src/explore.ts +21 -90
  624. package/src/external-clis.yaml +0 -25
  625. package/src/external.test.ts +9 -0
  626. package/src/external.ts +12 -10
  627. package/src/generate.ts +4 -41
  628. package/src/hooks.test.ts +126 -0
  629. package/src/hooks.ts +90 -0
  630. package/src/interceptor.ts +73 -23
  631. package/src/main.ts +2 -0
  632. package/src/output.ts +14 -6
  633. package/src/pipeline/executor.ts +1 -1
  634. package/src/pipeline/steps/browser.ts +1 -3
  635. package/src/pipeline/steps/download.test.ts +136 -0
  636. package/src/pipeline/steps/download.ts +47 -34
  637. package/src/pipeline/steps/fetch.test.ts +179 -0
  638. package/src/pipeline/steps/fetch.ts +39 -23
  639. package/src/pipeline/steps/transform.ts +2 -6
  640. package/src/pipeline/template.test.ts +28 -0
  641. package/src/pipeline/template.ts +67 -79
  642. package/src/pipeline/transform.test.ts +20 -0
  643. package/src/plugin.test.ts +251 -3
  644. package/src/plugin.ts +265 -21
  645. package/src/record.ts +12 -84
  646. package/src/registry-api.ts +2 -0
  647. package/src/registry.ts +7 -4
  648. package/src/runtime.ts +14 -4
  649. package/src/snapshotFormatter.ts +43 -121
  650. package/src/utils.ts +39 -0
  651. package/src/validate.ts +3 -5
  652. package/src/weread-search-regression.test.ts +44 -0
  653. package/src/yaml-schema.ts +28 -0
  654. package/tests/e2e/browser-auth.test.ts +25 -0
  655. package/tests/e2e/browser-public-extended.test.ts +162 -0
  656. package/tests/e2e/browser-public.test.ts +7 -146
  657. package/tests/e2e/plugin-management.test.ts +137 -0
  658. package/tests/e2e/public-commands.test.ts +34 -1
  659. package/vitest.config.ts +33 -8
  660. package/.github/workflows/pkg-pr-new.yml +0 -30
  661. package/dist/clis/douban/shared.d.ts +0 -4
  662. package/dist/clis/douban/shared.js +0 -155
  663. package/src/clis/douban/shared.ts +0 -165
  664. /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
  665. /package/dist/clis/boss/{common.js → utils.js} +0 -0
  666. /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
  667. /package/dist/clis/doubao/{common.js → utils.js} +0 -0
  668. /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
  669. /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
  670. /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
  671. /package/dist/clis/jike/{shared.js → utils.js} +0 -0
  672. /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
  673. /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
  674. /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
  675. /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
  676. /package/src/clis/boss/{common.ts → utils.ts} +0 -0
  677. /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
  678. /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
  679. /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
  680. /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
@@ -8,6 +8,42 @@
8
8
  * - stepIntercept (pipeline/steps/intercept.ts)
9
9
  * - stepTap (pipeline/steps/tap.ts)
10
10
  */
11
+ /**
12
+ * Helper: define a non-enumerable property on window.
13
+ * Avoids detection via Object.keys(window) or for..in loops.
14
+ */
15
+ const DEFINE_HIDDEN = `
16
+ function __defHidden(obj, key, val) {
17
+ try {
18
+ Object.defineProperty(obj, key, { value: val, writable: true, enumerable: false, configurable: true });
19
+ } catch { obj[key] = val; }
20
+ }`;
21
+ /**
22
+ * Helper: disguise a patched function so toString() returns native code signature.
23
+ */
24
+ const DISGUISE_FN = `
25
+ function __disguise(fn, name) {
26
+ const nativeStr = 'function ' + name + '() { [native code] }';
27
+ // Override toString on the instance AND patch Function.prototype.toString
28
+ // to handle Function.prototype.toString.call(fn) bypasses.
29
+ const _origToString = Function.prototype.toString;
30
+ const _patchedFns = window.__dFns || (function() {
31
+ const m = new Map();
32
+ Object.defineProperty(window, '__dFns', { value: m, enumerable: false, configurable: true });
33
+ // Patch Function.prototype.toString once to consult the map
34
+ Object.defineProperty(Function.prototype, 'toString', {
35
+ value: function() {
36
+ const override = m.get(this);
37
+ return override !== undefined ? override : _origToString.call(this);
38
+ },
39
+ writable: true, configurable: true
40
+ });
41
+ return m;
42
+ })();
43
+ _patchedFns.set(fn, nativeStr);
44
+ try { Object.defineProperty(fn, 'name', { value: name, configurable: true }); } catch {}
45
+ return fn;
46
+ }`;
11
47
  /**
12
48
  * Generate JavaScript source that installs a fetch/XHR interceptor.
13
49
  * Captured responses are pushed to `window.__opencli_intercepted`.
@@ -19,18 +55,23 @@
19
55
  export function generateInterceptorJs(patternExpr, opts = {}) {
20
56
  const arr = opts.arrayName ?? '__opencli_intercepted';
21
57
  const guard = opts.patchGuard ?? '__opencli_interceptor_patched';
58
+ // Store the current pattern in a separate global so it can be updated
59
+ // without re-patching fetch/XHR (the patchGuard only prevents double-patching).
60
+ const patternVar = `${guard}_pattern`;
22
61
  return `
23
62
  () => {
24
- window.${arr} = window.${arr} || [];
25
- window.${arr}_errors = window.${arr}_errors || [];
26
- const __pattern = ${patternExpr};
63
+ ${DEFINE_HIDDEN}
64
+ ${DISGUISE_FN}
27
65
 
28
- if (!window.${guard}) {
29
- const __checkMatch = (url) => __pattern && url.includes(__pattern);
66
+ if (!window.${arr}) __defHidden(window, '${arr}', []);
67
+ if (!window.${arr}_errors) __defHidden(window, '${arr}_errors', []);
68
+ __defHidden(window, '${patternVar}', ${patternExpr});
69
+ const __checkMatch = (url) => window.${patternVar} && url.includes(window.${patternVar});
30
70
 
71
+ if (!window.${guard}) {
31
72
  // ── Patch fetch ──
32
73
  const __origFetch = window.fetch;
33
- window.fetch = async function(...args) {
74
+ window.fetch = __disguise(async function(...args) {
34
75
  const reqUrl = typeof args[0] === 'string' ? args[0]
35
76
  : (args[0] && args[0].url) || '';
36
77
  const response = await __origFetch.apply(this, args);
@@ -42,28 +83,28 @@ export function generateInterceptorJs(patternExpr, opts = {}) {
42
83
  } catch(e) { window.${arr}_errors.push({ url: reqUrl, error: String(e) }); }
43
84
  }
44
85
  return response;
45
- };
86
+ }, 'fetch');
46
87
 
47
88
  // ── Patch XMLHttpRequest ──
48
89
  const __XHR = XMLHttpRequest.prototype;
49
90
  const __origOpen = __XHR.open;
50
91
  const __origSend = __XHR.send;
51
- __XHR.open = function(method, url) {
52
- this.__opencli_url = String(url);
92
+ __XHR.open = __disguise(function(method, url) {
93
+ Object.defineProperty(this, '__iurl', { value: String(url), writable: true, enumerable: false, configurable: true });
53
94
  return __origOpen.apply(this, arguments);
54
- };
55
- __XHR.send = function() {
56
- if (__checkMatch(this.__opencli_url)) {
95
+ }, 'open');
96
+ __XHR.send = __disguise(function() {
97
+ if (__checkMatch(this.__iurl)) {
57
98
  this.addEventListener('load', function() {
58
99
  try {
59
100
  window.${arr}.push(JSON.parse(this.responseText));
60
- } catch(e) { window.${arr}_errors.push({ url: this.__opencli_url, error: String(e) }); }
101
+ } catch(e) { window.${arr}_errors.push({ url: this.__iurl, error: String(e) }); }
61
102
  });
62
103
  }
63
104
  return __origSend.apply(this, arguments);
64
- };
105
+ }, 'send');
65
106
 
66
- window.${guard} = true;
107
+ __defHidden(window, '${guard}', true);
67
108
  }
68
109
  }
69
110
  `;
@@ -94,13 +135,19 @@ export function generateTapInterceptorJs(patternExpr) {
94
135
  let captureResolve;
95
136
  const capturePromise = new Promise(r => { captureResolve = r; });
96
137
  const capturePattern = ${patternExpr};
138
+ function __disguise(fn, name) {
139
+ const s = 'function ' + name + '() { [native code] }';
140
+ Object.defineProperty(fn, 'toString', { value: function() { return s; }, writable: true, configurable: true, enumerable: false });
141
+ try { Object.defineProperty(fn, 'name', { value: name, configurable: true }); } catch {}
142
+ return fn;
143
+ }
97
144
  `,
98
145
  capturedVar: 'captured',
99
146
  promiseVar: 'capturePromise',
100
147
  resolveVar: 'captureResolve',
101
148
  fetchPatch: `
102
149
  const origFetch = window.fetch;
103
- window.fetch = async function(...fetchArgs) {
150
+ window.fetch = __disguise(async function(...fetchArgs) {
104
151
  const resp = await origFetch.apply(this, fetchArgs);
105
152
  try {
106
153
  const url = typeof fetchArgs[0] === 'string' ? fetchArgs[0]
@@ -110,17 +157,17 @@ export function generateTapInterceptorJs(patternExpr) {
110
157
  }
111
158
  } catch {}
112
159
  return resp;
113
- };
160
+ }, 'fetch');
114
161
  `,
115
162
  xhrPatch: `
116
163
  const origXhrOpen = XMLHttpRequest.prototype.open;
117
164
  const origXhrSend = XMLHttpRequest.prototype.send;
118
- XMLHttpRequest.prototype.open = function(method, url) {
119
- this.__tapUrl = String(url);
165
+ XMLHttpRequest.prototype.open = __disguise(function(method, url) {
166
+ Object.defineProperty(this, '__iurl', { value: String(url), writable: true, enumerable: false, configurable: true });
120
167
  return origXhrOpen.apply(this, arguments);
121
- };
122
- XMLHttpRequest.prototype.send = function(body) {
123
- if (capturePattern && this.__tapUrl?.includes(capturePattern)) {
168
+ }, 'open');
169
+ XMLHttpRequest.prototype.send = __disguise(function(body) {
170
+ if (capturePattern && this.__iurl?.includes(capturePattern)) {
124
171
  this.addEventListener('load', function() {
125
172
  if (!captured) {
126
173
  try { captured = JSON.parse(this.responseText); captureResolve(); } catch {}
@@ -128,7 +175,7 @@ export function generateTapInterceptorJs(patternExpr) {
128
175
  });
129
176
  }
130
177
  return origXhrSend.apply(this, arguments);
131
- };
178
+ }, 'send');
132
179
  `,
133
180
  restorePatch: `
134
181
  window.fetch = origFetch;
package/dist/main.js CHANGED
@@ -18,6 +18,7 @@ import { fileURLToPath } from 'node:url';
18
18
  import { discoverClis, discoverPlugins } from './discovery.js';
19
19
  import { getCompletions } from './completion.js';
20
20
  import { runCli } from './cli.js';
21
+ import { emitHook } from './hooks.js';
21
22
  const __filename = fileURLToPath(import.meta.url);
22
23
  const __dirname = path.dirname(__filename);
23
24
  const BUILTIN_CLIS = path.resolve(__dirname, 'clis');
@@ -46,4 +47,5 @@ if (getCompIdx !== -1) {
46
47
  process.stdout.write(candidates.join('\n') + '\n');
47
48
  process.exit(0);
48
49
  }
50
+ await emitHook('onStartup', { command: '__startup__', args: {} });
49
51
  runCli(BUILTIN_CLIS, USER_CLIS);
package/dist/output.js CHANGED
@@ -4,6 +4,12 @@
4
4
  import chalk from 'chalk';
5
5
  import Table from 'cli-table3';
6
6
  import yaml from 'js-yaml';
7
+ function normalizeRows(data) {
8
+ return Array.isArray(data) ? data : [data];
9
+ }
10
+ function resolveColumns(rows, opts) {
11
+ return opts.columns ?? Object.keys(rows[0] ?? {});
12
+ }
7
13
  export function render(data, opts = {}) {
8
14
  const fmt = opts.fmt ?? 'table';
9
15
  if (data === null || data === undefined) {
@@ -31,12 +37,12 @@ export function render(data, opts = {}) {
31
37
  }
32
38
  }
33
39
  function renderTable(data, opts) {
34
- const rows = Array.isArray(data) ? data : [data];
40
+ const rows = normalizeRows(data);
35
41
  if (!rows.length) {
36
42
  console.log(chalk.dim('(no data)'));
37
43
  return;
38
44
  }
39
- const columns = opts.columns ?? Object.keys(rows[0]);
45
+ const columns = resolveColumns(rows, opts);
40
46
  const header = columns.map(c => capitalize(c));
41
47
  const table = new Table({
42
48
  head: header.map(h => chalk.bold(h)),
@@ -68,10 +74,10 @@ function renderJson(data) {
68
74
  console.log(JSON.stringify(data, null, 2));
69
75
  }
70
76
  function renderMarkdown(data, opts) {
71
- const rows = Array.isArray(data) ? data : [data];
77
+ const rows = normalizeRows(data);
72
78
  if (!rows.length)
73
79
  return;
74
- const columns = opts.columns ?? Object.keys(rows[0]);
80
+ const columns = resolveColumns(rows, opts);
75
81
  console.log('| ' + columns.join(' | ') + ' |');
76
82
  console.log('| ' + columns.map(() => '---').join(' | ') + ' |');
77
83
  for (const row of rows) {
@@ -79,10 +85,10 @@ function renderMarkdown(data, opts) {
79
85
  }
80
86
  }
81
87
  function renderCsv(data, opts) {
82
- const rows = Array.isArray(data) ? data : [data];
88
+ const rows = normalizeRows(data);
83
89
  if (!rows.length)
84
90
  return;
85
- const columns = opts.columns ?? Object.keys(rows[0]);
91
+ const columns = resolveColumns(rows, opts);
86
92
  console.log(columns.join(','));
87
93
  for (const row of rows) {
88
94
  console.log(columns.map(c => {
@@ -5,7 +5,7 @@ import { getStep } from './registry.js';
5
5
  import { log } from '../logger.js';
6
6
  import { ConfigError } from '../errors.js';
7
7
  /** Steps that interact with the browser and may fail transiently */
8
- const BROWSER_STEPS = new Set(['navigate', 'evaluate', 'click', 'type', 'press', 'wait', 'snapshot', 'scroll']);
8
+ const BROWSER_STEPS = new Set(['navigate', 'evaluate', 'click', 'type', 'press', 'wait', 'snapshot']);
9
9
  export async function executePipeline(page, pipeline, ctx = {}) {
10
10
  const args = ctx.args ?? {};
11
11
  const debug = ctx.debug ?? false;
@@ -3,9 +3,7 @@
3
3
  * Browser interaction primitives.
4
4
  */
5
5
  import { render } from '../template.js';
6
- function isRecord(value) {
7
- return typeof value === 'object' && value !== null && !Array.isArray(value);
8
- }
6
+ import { isRecord } from '../../utils.js';
9
7
  export async function stepNavigate(page, params, data, args) {
10
8
  if (isRecord(params) && 'url' in params) {
11
9
  const url = String(render(params.url, { args, data }));
@@ -10,30 +10,16 @@
10
10
  import * as fs from 'node:fs';
11
11
  import * as path from 'node:path';
12
12
  import { render } from '../template.js';
13
+ import { getErrorMessage } from '../../errors.js';
13
14
  import { httpDownload, ytdlpDownload, saveDocument, detectContentType, requiresYtdlp, sanitizeFilename, generateFilename, exportCookiesToNetscape, getTempDir, formatCookieHeader, } from '../../download/index.js';
14
15
  import { DownloadProgressTracker, formatBytes } from '../../download/progress.js';
15
- /**
16
- * Simple async concurrency limiter for downloads.
17
- */
18
- async function mapConcurrent(items, limit, fn) {
19
- const results = new Array(items.length);
20
- let index = 0;
21
- async function worker() {
22
- while (index < items.length) {
23
- const i = index++;
24
- results[i] = await fn(items[i], i);
25
- }
26
- }
27
- const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
28
- await Promise.all(workers);
29
- return results;
30
- }
16
+ import { mapConcurrent } from '../../utils.js';
31
17
  /**
32
18
  * Extract cookies from browser page.
33
19
  */
34
20
  async function extractBrowserCookies(page, domain) {
35
21
  try {
36
- const cookies = await page.getCookies(domain ? { domain } : {});
22
+ const cookies = await page.getCookies({ domain });
37
23
  return formatCookieHeader(cookies);
38
24
  }
39
25
  catch {
@@ -61,6 +47,13 @@ async function extractCookiesArray(page, domain) {
61
47
  return [];
62
48
  }
63
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
+ }
64
57
  /**
65
58
  * Download step handler for YAML pipelines.
66
59
  *
@@ -101,21 +94,28 @@ export async function stepDownload(page, params, data, args) {
101
94
  }
102
95
  // Create progress tracker
103
96
  const tracker = new DownloadProgressTracker(items.length, showProgress);
104
- // Extract cookies if browser is available
105
- let cookies = '';
97
+ // Cache cookie lookups per domain so mixed-domain batches stay isolated without repeated browser calls.
98
+ const cookieHeaderCache = new Map();
106
99
  let cookiesFile;
107
100
  if (page) {
108
- cookies = await extractBrowserCookies(page);
109
101
  // For yt-dlp, we need to export cookies to Netscape format
110
102
  if (useYtdlp || items.some((item, index) => {
111
103
  const url = String(render(urlTemplate, { args, data, item, index }));
112
104
  return requiresYtdlp(url);
113
105
  })) {
114
106
  try {
115
- // Try to get domain from first URL
116
- const firstUrl = String(render(urlTemplate, { args, data, item: items[0], index: 0 }));
117
- const domain = new URL(firstUrl).hostname;
118
- const cookiesArray = await extractCookiesArray(page, domain);
107
+ const ytdlpDomains = [...new Set(items.flatMap((item, index) => {
108
+ const url = String(render(urlTemplate, { args, data, item, index }));
109
+ if (!useYtdlp && !requiresYtdlp(url))
110
+ return [];
111
+ try {
112
+ return [new URL(url).hostname];
113
+ }
114
+ catch {
115
+ return [];
116
+ }
117
+ }))];
118
+ const cookiesArray = dedupeCookies((await Promise.all(ytdlpDomains.map((domain) => extractCookiesArray(page, domain)))).flat());
119
119
  if (cookiesArray.length > 0) {
120
120
  const tempDir = getTempDir();
121
121
  fs.mkdirSync(tempDir, { recursive: true });
@@ -199,6 +199,21 @@ export async function stepDownload(page, params, data, args) {
199
199
  }
200
200
  else {
201
201
  // Direct HTTP download
202
+ let cookies = '';
203
+ if (page) {
204
+ try {
205
+ const targetDomain = new URL(url).hostname;
206
+ let cookiePromise = cookieHeaderCache.get(targetDomain);
207
+ if (!cookiePromise) {
208
+ cookiePromise = extractBrowserCookies(page, targetDomain);
209
+ cookieHeaderCache.set(targetDomain, cookiePromise);
210
+ }
211
+ cookies = await cookiePromise;
212
+ }
213
+ catch {
214
+ cookies = '';
215
+ }
216
+ }
202
217
  result = await httpDownload(url, destPath, {
203
218
  cookies,
204
219
  timeout,
@@ -214,9 +229,10 @@ export async function stepDownload(page, params, data, args) {
214
229
  }
215
230
  }
216
231
  catch (err) {
217
- result = { success: false, size: 0, error: err.message };
232
+ const msg = getErrorMessage(err);
233
+ result = { success: false, size: 0, error: msg };
218
234
  if (progressBar) {
219
- progressBar.fail(err.message);
235
+ progressBar.fail(msg);
220
236
  }
221
237
  }
222
238
  tracker.onFileComplete(result.success);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,101 @@
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
+ getCookies,
24
+ snapshot: vi.fn().mockResolvedValue(''),
25
+ click: vi.fn(),
26
+ typeText: vi.fn(),
27
+ pressKey: vi.fn(),
28
+ scrollTo: vi.fn(),
29
+ getFormState: vi.fn().mockResolvedValue({}),
30
+ wait: vi.fn(),
31
+ tabs: vi.fn().mockResolvedValue([]),
32
+ closeTab: vi.fn(),
33
+ newTab: vi.fn(),
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
+ };
43
+ }
44
+ describe('stepDownload', () => {
45
+ beforeEach(() => {
46
+ mockHttpDownload.mockReset();
47
+ mockHttpDownload.mockResolvedValue({ success: true, size: 2 });
48
+ mockYtdlpDownload.mockReset();
49
+ mockYtdlpDownload.mockResolvedValue({ success: true, size: 2 });
50
+ mockExportCookiesToNetscape.mockReset();
51
+ });
52
+ it('scopes browser cookies to each direct-download target domain', async () => {
53
+ const page = createMockPage(vi.fn().mockImplementation(async (opts) => {
54
+ const domain = opts?.domain ?? 'unknown';
55
+ return [{ name: 'sid', value: domain, domain }];
56
+ }));
57
+ await stepDownload(page, {
58
+ url: '${{ item.url }}',
59
+ dir: path.join(os.tmpdir(), 'opencli-download-test'),
60
+ filename: '${{ index }}.txt',
61
+ progress: false,
62
+ concurrency: 1,
63
+ }, [
64
+ { url: 'https://a.example/file-1.txt' },
65
+ { url: 'https://b.example/file-2.txt' },
66
+ ], {});
67
+ expect(mockHttpDownload).toHaveBeenNthCalledWith(1, 'https://a.example/file-1.txt', path.join(os.tmpdir(), 'opencli-download-test', '0.txt'), expect.objectContaining({ cookies: 'sid=a.example' }));
68
+ expect(mockHttpDownload).toHaveBeenNthCalledWith(2, 'https://b.example/file-2.txt', path.join(os.tmpdir(), 'opencli-download-test', '1.txt'), expect.objectContaining({ cookies: 'sid=b.example' }));
69
+ });
70
+ it('builds yt-dlp cookies from all target domains instead of only the first item', async () => {
71
+ const getCookies = vi.fn().mockImplementation(async (opts) => {
72
+ const domain = opts?.domain ?? 'unknown';
73
+ return [{
74
+ name: `sid-${domain}`,
75
+ value: domain,
76
+ domain,
77
+ path: '/',
78
+ secure: false,
79
+ httpOnly: false,
80
+ }];
81
+ });
82
+ const page = createMockPage(getCookies);
83
+ await stepDownload(page, {
84
+ url: '${{ item.url }}',
85
+ dir: '/tmp/opencli-download-test',
86
+ filename: '${{ index }}.mp4',
87
+ progress: false,
88
+ concurrency: 1,
89
+ }, [
90
+ { url: 'https://www.youtube.com/watch?v=one' },
91
+ { url: 'https://www.bilibili.com/video/BV1xx411c7mD' },
92
+ ], {});
93
+ expect(getCookies).toHaveBeenCalledWith({ domain: 'www.youtube.com' });
94
+ expect(getCookies).toHaveBeenCalledWith({ domain: 'www.bilibili.com' });
95
+ expect(mockExportCookiesToNetscape).toHaveBeenCalledWith(expect.arrayContaining([
96
+ expect.objectContaining({ name: 'sid-www.youtube.com', domain: 'www.youtube.com' }),
97
+ expect.objectContaining({ name: 'sid-www.bilibili.com', domain: 'www.bilibili.com' }),
98
+ ]), expect.any(String));
99
+ expect(mockYtdlpDownload).toHaveBeenCalledTimes(2);
100
+ });
101
+ });
@@ -1,24 +1,10 @@
1
1
  /**
2
2
  * Pipeline step: fetch — HTTP API requests.
3
3
  */
4
+ import { CliError, getErrorMessage } from '../../errors.js';
5
+ import { log } from '../../logger.js';
4
6
  import { render } from '../template.js';
5
- function isRecord(value) {
6
- return typeof value === 'object' && value !== null && !Array.isArray(value);
7
- }
8
- /** Simple async concurrency limiter */
9
- async function mapConcurrent(items, limit, fn) {
10
- const results = new Array(items.length);
11
- let index = 0;
12
- async function worker() {
13
- while (index < items.length) {
14
- const i = index++;
15
- results[i] = await fn(items[i], i);
16
- }
17
- }
18
- const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
19
- await Promise.all(workers);
20
- return results;
21
- }
7
+ import { isRecord, mapConcurrent } from '../../utils.js';
22
8
  /** Single URL fetch helper */
23
9
  async function fetchSingle(page, url, method, queryParams, headers, args, data) {
24
10
  const renderedParams = {};
@@ -34,19 +20,32 @@ async function fetchSingle(page, url, method, queryParams, headers, args, data)
34
20
  }
35
21
  if (page === null) {
36
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
+ }
37
26
  return resp.json();
38
27
  }
39
28
  const headersJs = JSON.stringify(renderedHeaders);
40
29
  const urlJs = JSON.stringify(finalUrl);
41
30
  const methodJs = JSON.stringify(method.toUpperCase());
42
- return page.evaluate(`
31
+ // Return error status instead of throwing inside evaluate to avoid CDP wrapper
32
+ // rewriting the message (CDP prepends "Evaluate error: " to thrown errors).
33
+ const result = await page.evaluate(`
43
34
  async () => {
44
35
  const resp = await fetch(${urlJs}, {
45
36
  method: ${methodJs}, headers: ${headersJs}, credentials: "include"
46
37
  });
38
+ if (!resp.ok) {
39
+ return { __httpError: resp.status, statusText: resp.statusText };
40
+ }
47
41
  return await resp.json();
48
42
  }
49
43
  `);
44
+ if (result && typeof result === 'object' && '__httpError' in result) {
45
+ const { __httpError: status, statusText } = result;
46
+ throw new CliError('FETCH_ERROR', `HTTP ${status} ${statusText} from ${finalUrl}`);
47
+ }
48
+ return result;
50
49
  }
51
50
  /**
52
51
  * Batch fetch: send all URLs into the browser as a single evaluate() call.
@@ -56,10 +55,11 @@ async function fetchSingle(page, url, method, queryParams, headers, args, data)
56
55
  async function fetchBatchInBrowser(page, urls, method, headers, concurrency) {
57
56
  const headersJs = JSON.stringify(headers);
58
57
  const urlsJs = JSON.stringify(urls);
58
+ const methodJs = JSON.stringify(method);
59
59
  return (await page.evaluate(`
60
60
  async () => {
61
61
  const urls = ${urlsJs};
62
- const method = "${method}";
62
+ const method = ${methodJs};
63
63
  const headers = ${headersJs};
64
64
  const concurrency = ${concurrency};
65
65
 
@@ -71,9 +71,13 @@ async function fetchBatchInBrowser(page, urls, method, headers, concurrency) {
71
71
  const i = idx++;
72
72
  try {
73
73
  const resp = await fetch(urls[i], { method, headers, credentials: "include" });
74
+ if (!resp.ok) {
75
+ throw new Error('HTTP ' + resp.status + ' ' + resp.statusText + ' from ' + urls[i]);
76
+ }
74
77
  results[i] = await resp.json();
75
78
  } catch (e) {
76
- results[i] = { error: e.message };
79
+ results[i] = { error: e instanceof Error ? e.message : String(e) };
80
+ // Note: getErrorMessage() is a Node.js utility — can't use it inside evaluate()
77
81
  }
78
82
  }
79
83
  }
@@ -111,12 +115,26 @@ export async function stepFetch(page, params, data, args) {
111
115
  });
112
116
  // BATCH IPC: if browser is available, batch all fetches into a single evaluate() call
113
117
  if (page !== null) {
114
- return fetchBatchInBrowser(page, urls, method.toUpperCase(), renderedHeaders, concurrency);
118
+ const results = await fetchBatchInBrowser(page, urls, method.toUpperCase(), renderedHeaders, concurrency);
119
+ for (let i = 0; i < results.length; i++) {
120
+ const r = results[i];
121
+ if (r && typeof r === 'object' && 'error' in r) {
122
+ log.warn(`Batch fetch failed for ${urls[i]}: ${r.error}`);
123
+ }
124
+ }
125
+ return results;
115
126
  }
116
127
  // Non-browser: use concurrent pool (already optimized)
117
128
  return mapConcurrent(data, concurrency, async (item, index) => {
118
129
  const itemUrl = String(render(urlTemplate, { args, data, item, index }));
119
- return fetchSingle(null, itemUrl, method, queryParams, headers, args, data);
130
+ try {
131
+ return await fetchSingle(null, itemUrl, method, queryParams, headers, args, data);
132
+ }
133
+ catch (error) {
134
+ const message = getErrorMessage(error);
135
+ log.warn(`Batch fetch failed for ${itemUrl}: ${message}`);
136
+ return { error: message };
137
+ }
120
138
  });
121
139
  }
122
140
  const url = render(urlOrObj, { args, data });
@@ -0,0 +1 @@
1
+ export {};