@jackwener/opencli 1.1.0 → 1.2.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 (769) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
  2. package/.github/pull_request_template.md +7 -0
  3. package/.github/workflows/doc-check.yml +36 -0
  4. package/.github/workflows/docs.yml +7 -42
  5. package/CHANGELOG.md +23 -0
  6. package/CLI-EXPLORER.md +9 -8
  7. package/CONTRIBUTING.md +39 -1
  8. package/README.md +33 -19
  9. package/README.zh-CN.md +64 -27
  10. package/SKILL.md +102 -33
  11. package/dist/browser/cdp.d.ts +4 -4
  12. package/dist/browser/cdp.js +45 -17
  13. package/dist/browser/daemon-client.d.ts +2 -1
  14. package/dist/browser/dom-helpers.js +38 -7
  15. package/dist/browser/dom-snapshot.d.ts +86 -0
  16. package/dist/browser/dom-snapshot.js +729 -0
  17. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  18. package/dist/browser/dom-snapshot.test.js +212 -0
  19. package/dist/browser/index.d.ts +2 -0
  20. package/dist/browser/index.js +1 -0
  21. package/dist/browser/page.d.ts +18 -25
  22. package/dist/browser/page.js +44 -5
  23. package/dist/build-manifest.d.ts +11 -4
  24. package/dist/build-manifest.js +79 -34
  25. package/dist/build-manifest.test.js +58 -2
  26. package/dist/cli-manifest.json +4273 -1771
  27. package/dist/cli.d.ts +6 -0
  28. package/dist/cli.js +255 -162
  29. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  30. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  31. package/dist/clis/apple-podcasts/search.js +2 -2
  32. package/dist/clis/apple-podcasts/top.js +9 -2
  33. package/dist/clis/arxiv/search.js +1 -1
  34. package/dist/clis/barchart/greeks.js +1 -1
  35. package/dist/clis/barchart/options.js +1 -1
  36. package/dist/clis/barchart/quote.js +1 -1
  37. package/dist/clis/bilibili/download.js +1 -1
  38. package/dist/clis/bilibili/dynamic.js +1 -1
  39. package/dist/clis/bilibili/favorite.js +1 -1
  40. package/dist/clis/bilibili/feed.js +1 -1
  41. package/dist/clis/bilibili/following.js +2 -2
  42. package/dist/clis/bilibili/history.js +1 -1
  43. package/dist/clis/bilibili/me.js +1 -1
  44. package/dist/clis/bilibili/ranking.js +1 -1
  45. package/dist/clis/bilibili/search.js +3 -3
  46. package/dist/clis/bilibili/subtitle.js +2 -2
  47. package/dist/clis/bilibili/user-videos.js +2 -2
  48. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  49. package/dist/clis/bloomberg/businessweek.js +17 -0
  50. package/dist/clis/bloomberg/economics.js +17 -0
  51. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  52. package/dist/clis/bloomberg/feeds.js +15 -0
  53. package/dist/clis/bloomberg/industries.d.ts +1 -0
  54. package/dist/clis/bloomberg/industries.js +17 -0
  55. package/dist/clis/bloomberg/main.d.ts +1 -0
  56. package/dist/clis/bloomberg/main.js +17 -0
  57. package/dist/clis/bloomberg/markets.d.ts +1 -0
  58. package/dist/clis/bloomberg/markets.js +17 -0
  59. package/dist/clis/bloomberg/news.d.ts +1 -0
  60. package/dist/clis/bloomberg/news.js +105 -0
  61. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  62. package/dist/clis/bloomberg/opinions.js +17 -0
  63. package/dist/clis/bloomberg/politics.d.ts +1 -0
  64. package/dist/clis/bloomberg/politics.js +17 -0
  65. package/dist/clis/bloomberg/tech.d.ts +1 -0
  66. package/dist/clis/bloomberg/tech.js +17 -0
  67. package/dist/clis/bloomberg/utils.d.ts +34 -0
  68. package/dist/clis/bloomberg/utils.js +364 -0
  69. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  70. package/dist/clis/bloomberg/utils.test.js +129 -0
  71. package/dist/clis/boss/batchgreet.js +12 -99
  72. package/dist/clis/boss/chatlist.js +9 -26
  73. package/dist/clis/boss/chatmsg.js +11 -42
  74. package/dist/clis/boss/common.d.ts +92 -0
  75. package/dist/clis/boss/common.js +223 -0
  76. package/dist/clis/boss/detail.js +8 -50
  77. package/dist/clis/boss/exchange.js +13 -79
  78. package/dist/clis/boss/greet.js +20 -147
  79. package/dist/clis/boss/invite.js +26 -121
  80. package/dist/clis/boss/joblist.js +6 -31
  81. package/dist/clis/boss/mark.js +12 -85
  82. package/dist/clis/boss/recommend.js +10 -49
  83. package/dist/clis/boss/resume.js +18 -118
  84. package/dist/clis/boss/search.js +13 -61
  85. package/dist/clis/boss/send.js +18 -152
  86. package/dist/clis/boss/stats.js +20 -71
  87. package/dist/clis/chaoxing/assignments.js +1 -1
  88. package/dist/clis/chaoxing/exams.js +1 -1
  89. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  90. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  91. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  92. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  93. package/dist/clis/chatgpt/read.js +1 -1
  94. package/dist/clis/chatwise/export.js +1 -1
  95. package/dist/clis/chatwise/model.js +2 -2
  96. package/dist/clis/chatwise/screenshot.js +1 -1
  97. package/dist/clis/codex/export.js +1 -1
  98. package/dist/clis/codex/model.js +2 -2
  99. package/dist/clis/codex/screenshot.js +1 -1
  100. package/dist/clis/coupang/add-to-cart.js +3 -4
  101. package/dist/clis/coupang/search.js +2 -4
  102. package/dist/clis/coupang/utils.test.d.ts +1 -0
  103. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  104. package/dist/clis/ctrip/search.js +1 -1
  105. package/dist/clis/cursor/export.js +1 -1
  106. package/dist/clis/cursor/model.js +2 -2
  107. package/dist/clis/cursor/screenshot.js +1 -1
  108. package/dist/clis/devto/tag.yaml +34 -0
  109. package/dist/clis/devto/top.yaml +29 -0
  110. package/dist/clis/devto/user.yaml +33 -0
  111. package/dist/clis/douban/book-hot.d.ts +1 -0
  112. package/dist/clis/douban/book-hot.js +14 -0
  113. package/dist/clis/douban/marks.d.ts +1 -0
  114. package/dist/clis/douban/marks.js +115 -0
  115. package/dist/clis/douban/movie-hot.d.ts +1 -0
  116. package/dist/clis/douban/movie-hot.js +14 -0
  117. package/dist/clis/douban/reviews.d.ts +1 -0
  118. package/dist/clis/douban/reviews.js +106 -0
  119. package/dist/clis/douban/search.d.ts +1 -0
  120. package/dist/clis/douban/search.js +16 -0
  121. package/dist/clis/douban/shared.d.ts +4 -0
  122. package/dist/clis/douban/shared.js +155 -0
  123. package/dist/clis/douban/subject.yaml +76 -0
  124. package/dist/clis/douban/top250.yaml +70 -0
  125. package/dist/clis/douban/utils.d.ts +35 -0
  126. package/dist/clis/douban/utils.js +48 -0
  127. package/dist/clis/facebook/add-friend.yaml +43 -0
  128. package/dist/clis/facebook/events.yaml +44 -0
  129. package/dist/clis/facebook/feed.yaml +63 -0
  130. package/dist/clis/facebook/friends.yaml +42 -0
  131. package/dist/clis/facebook/groups.yaml +50 -0
  132. package/dist/clis/facebook/join-group.yaml +44 -0
  133. package/dist/clis/facebook/memories.yaml +39 -0
  134. package/dist/clis/facebook/notifications.yaml +40 -0
  135. package/dist/clis/facebook/profile.yaml +37 -0
  136. package/dist/clis/facebook/search.yaml +46 -0
  137. package/dist/clis/google/news.d.ts +5 -0
  138. package/dist/clis/google/news.js +58 -0
  139. package/dist/clis/google/search.d.ts +10 -0
  140. package/dist/clis/google/search.js +127 -0
  141. package/dist/clis/google/suggest.d.ts +5 -0
  142. package/dist/clis/google/suggest.js +34 -0
  143. package/dist/clis/google/trends.d.ts +5 -0
  144. package/dist/clis/google/trends.js +38 -0
  145. package/dist/clis/google/utils.d.ts +9 -0
  146. package/dist/clis/google/utils.js +23 -0
  147. package/dist/clis/google/utils.test.d.ts +1 -0
  148. package/dist/clis/google/utils.test.js +75 -0
  149. package/dist/clis/grok/ask.d.ts +14 -0
  150. package/dist/clis/grok/ask.js +257 -65
  151. package/dist/clis/grok/ask.test.d.ts +1 -0
  152. package/dist/clis/grok/ask.test.js +36 -0
  153. package/dist/clis/instagram/comment.yaml +52 -0
  154. package/dist/clis/instagram/explore.yaml +43 -0
  155. package/dist/clis/instagram/follow.yaml +41 -0
  156. package/dist/clis/instagram/followers.yaml +51 -0
  157. package/dist/clis/instagram/following.yaml +51 -0
  158. package/dist/clis/instagram/like.yaml +46 -0
  159. package/dist/clis/instagram/profile.yaml +42 -0
  160. package/dist/clis/instagram/save.yaml +46 -0
  161. package/dist/clis/instagram/saved.yaml +40 -0
  162. package/dist/clis/instagram/search.yaml +43 -0
  163. package/dist/clis/instagram/unfollow.yaml +38 -0
  164. package/dist/clis/instagram/unlike.yaml +46 -0
  165. package/dist/clis/instagram/unsave.yaml +46 -0
  166. package/dist/clis/instagram/user.yaml +54 -0
  167. package/dist/clis/jike/comment.js +2 -3
  168. package/dist/clis/jike/create.js +1 -2
  169. package/dist/clis/jike/feed.js +0 -1
  170. package/dist/clis/jike/like.js +1 -2
  171. package/dist/clis/jike/notifications.js +0 -1
  172. package/dist/clis/jike/post.yaml +1 -0
  173. package/dist/clis/jike/repost.js +2 -3
  174. package/dist/clis/jike/search.js +2 -3
  175. package/dist/clis/jike/topic.yaml +1 -0
  176. package/dist/clis/jike/user.yaml +1 -0
  177. package/dist/clis/jimeng/generate.yaml +1 -0
  178. package/dist/clis/jimeng/history.yaml +0 -1
  179. package/dist/clis/linkedin/search.js +7 -7
  180. package/dist/clis/linux-do/category.yaml +2 -0
  181. package/dist/clis/linux-do/search.yaml +4 -3
  182. package/dist/clis/linux-do/topic.yaml +1 -0
  183. package/dist/clis/lobsters/active.yaml +29 -0
  184. package/dist/clis/lobsters/hot.yaml +29 -0
  185. package/dist/clis/lobsters/newest.yaml +29 -0
  186. package/dist/clis/lobsters/tag.yaml +34 -0
  187. package/dist/clis/medium/feed.d.ts +1 -0
  188. package/dist/clis/medium/feed.js +15 -0
  189. package/dist/clis/medium/search.d.ts +1 -0
  190. package/dist/clis/medium/search.js +15 -0
  191. package/dist/clis/medium/shared.d.ts +5 -0
  192. package/dist/clis/medium/shared.js +78 -0
  193. package/dist/clis/medium/user.d.ts +1 -0
  194. package/dist/clis/medium/user.js +15 -0
  195. package/dist/clis/notion/export.js +1 -1
  196. package/dist/clis/reddit/comment.js +3 -4
  197. package/dist/clis/reddit/read.js +4 -5
  198. package/dist/clis/reddit/save.js +2 -3
  199. package/dist/clis/reddit/saved.js +0 -1
  200. package/dist/clis/reddit/search.yaml +1 -0
  201. package/dist/clis/reddit/subreddit.yaml +1 -0
  202. package/dist/clis/reddit/subscribe.js +1 -2
  203. package/dist/clis/reddit/upvote.js +2 -3
  204. package/dist/clis/reddit/upvoted.js +0 -1
  205. package/dist/clis/reddit/user-comments.yaml +1 -0
  206. package/dist/clis/reddit/user-posts.yaml +1 -0
  207. package/dist/clis/reddit/user.yaml +1 -0
  208. package/dist/clis/reuters/search.js +1 -1
  209. package/dist/clis/sinablog/article.d.ts +1 -0
  210. package/dist/clis/sinablog/article.js +14 -0
  211. package/dist/clis/sinablog/hot.d.ts +1 -0
  212. package/dist/clis/sinablog/hot.js +14 -0
  213. package/dist/clis/sinablog/search.d.ts +1 -0
  214. package/dist/clis/sinablog/search.js +51 -0
  215. package/dist/clis/sinablog/shared.d.ts +7 -0
  216. package/dist/clis/sinablog/shared.js +187 -0
  217. package/dist/clis/sinablog/user.d.ts +1 -0
  218. package/dist/clis/sinablog/user.js +15 -0
  219. package/dist/clis/smzdm/search.js +2 -3
  220. package/dist/clis/stackoverflow/search.yaml +1 -0
  221. package/dist/clis/steam/top-sellers.yaml +29 -0
  222. package/dist/clis/substack/feed.d.ts +1 -0
  223. package/dist/clis/substack/feed.js +15 -0
  224. package/dist/clis/substack/publication.d.ts +1 -0
  225. package/dist/clis/substack/publication.js +15 -0
  226. package/dist/clis/substack/search.d.ts +1 -0
  227. package/dist/clis/substack/search.js +77 -0
  228. package/dist/clis/substack/shared.d.ts +4 -0
  229. package/dist/clis/substack/shared.js +129 -0
  230. package/dist/clis/tiktok/comment.yaml +66 -0
  231. package/dist/clis/tiktok/explore.yaml +39 -0
  232. package/dist/clis/tiktok/follow.yaml +39 -0
  233. package/dist/clis/tiktok/following.yaml +46 -0
  234. package/dist/clis/tiktok/friends.yaml +47 -0
  235. package/dist/clis/tiktok/like.yaml +38 -0
  236. package/dist/clis/tiktok/live.yaml +51 -0
  237. package/dist/clis/tiktok/notifications.yaml +52 -0
  238. package/dist/clis/tiktok/profile.yaml +45 -0
  239. package/dist/clis/tiktok/save.yaml +34 -0
  240. package/dist/clis/tiktok/search.yaml +46 -0
  241. package/dist/clis/tiktok/unfollow.yaml +44 -0
  242. package/dist/clis/tiktok/unlike.yaml +38 -0
  243. package/dist/clis/tiktok/unsave.yaml +36 -0
  244. package/dist/clis/tiktok/user.yaml +44 -0
  245. package/dist/clis/twitter/accept.js +2 -2
  246. package/dist/clis/twitter/article.js +2 -2
  247. package/dist/clis/twitter/block.d.ts +1 -0
  248. package/dist/clis/twitter/block.js +88 -0
  249. package/dist/clis/twitter/delete.js +1 -1
  250. package/dist/clis/twitter/download.d.ts +1 -1
  251. package/dist/clis/twitter/download.js +3 -3
  252. package/dist/clis/twitter/followers.js +1 -1
  253. package/dist/clis/twitter/following.js +1 -1
  254. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  255. package/dist/clis/twitter/hide-reply.js +66 -0
  256. package/dist/clis/twitter/like.js +1 -1
  257. package/dist/clis/twitter/post.js +1 -1
  258. package/dist/clis/twitter/reply-dm.js +1 -1
  259. package/dist/clis/twitter/reply.js +2 -2
  260. package/dist/clis/twitter/search.js +1 -1
  261. package/dist/clis/twitter/thread.js +2 -2
  262. package/dist/clis/twitter/timeline.d.ts +23 -0
  263. package/dist/clis/twitter/timeline.js +42 -14
  264. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  265. package/dist/clis/twitter/timeline.test.js +102 -0
  266. package/dist/clis/twitter/trending.d.ts +1 -0
  267. package/dist/clis/twitter/trending.js +91 -0
  268. package/dist/clis/twitter/unblock.d.ts +1 -0
  269. package/dist/clis/twitter/unblock.js +71 -0
  270. package/dist/clis/v2ex/topic.yaml +1 -0
  271. package/dist/clis/weibo/hot.js +0 -1
  272. package/dist/clis/weread/book.js +1 -1
  273. package/dist/clis/weread/highlights.js +1 -1
  274. package/dist/clis/weread/notes.js +1 -1
  275. package/dist/clis/weread/search.js +1 -1
  276. package/dist/clis/wikipedia/random.d.ts +1 -0
  277. package/dist/clis/wikipedia/random.js +19 -0
  278. package/dist/clis/wikipedia/search.js +4 -4
  279. package/dist/clis/wikipedia/summary.js +4 -9
  280. package/dist/clis/wikipedia/trending.d.ts +1 -0
  281. package/dist/clis/wikipedia/trending.js +35 -0
  282. package/dist/clis/wikipedia/utils.d.ts +28 -0
  283. package/dist/clis/wikipedia/utils.js +13 -0
  284. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  285. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  286. package/dist/clis/xiaohongshu/creator-note-detail.test.js +82 -33
  287. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  288. package/dist/clis/xiaohongshu/creator-notes.test.js +37 -6
  289. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  290. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  291. package/dist/clis/xiaohongshu/download.js +2 -3
  292. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  293. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  294. package/dist/clis/xiaohongshu/search.js +2 -2
  295. package/dist/clis/xiaohongshu/user.js +1 -2
  296. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  297. package/dist/clis/xueqiu/search.yaml +2 -1
  298. package/dist/clis/xueqiu/stock.yaml +2 -0
  299. package/dist/clis/yahoo-finance/quote.js +1 -2
  300. package/dist/clis/youtube/search.js +1 -1
  301. package/dist/clis/youtube/transcript.js +1 -1
  302. package/dist/clis/youtube/video.js +1 -1
  303. package/dist/clis/zhihu/download.js +1 -2
  304. package/dist/clis/zhihu/question.js +1 -1
  305. package/dist/clis/zhihu/search.yaml +4 -3
  306. package/dist/commanderAdapter.d.ts +21 -0
  307. package/dist/commanderAdapter.js +117 -0
  308. package/dist/{engine.d.ts → discovery.d.ts} +6 -4
  309. package/dist/{engine.js → discovery.js} +93 -104
  310. package/dist/doctor.js +3 -1
  311. package/dist/doctor.test.js +46 -2
  312. package/dist/download/index.d.ts +2 -6
  313. package/dist/download/index.js +19 -46
  314. package/dist/engine.test.d.ts +0 -3
  315. package/dist/engine.test.js +80 -11
  316. package/dist/execution.d.ts +24 -0
  317. package/dist/execution.js +153 -0
  318. package/dist/explore.d.ts +76 -3
  319. package/dist/explore.js +132 -111
  320. package/dist/external-clis.yaml +48 -0
  321. package/dist/external.d.ts +7 -2
  322. package/dist/external.js +11 -14
  323. package/dist/generate.d.ts +41 -2
  324. package/dist/generate.js +5 -4
  325. package/dist/main.js +2 -1
  326. package/dist/pipeline/executor.d.ts +2 -2
  327. package/dist/pipeline/executor.js +2 -2
  328. package/dist/pipeline/executor.test.js +33 -6
  329. package/dist/pipeline/registry.d.ts +1 -1
  330. package/dist/pipeline/steps/browser.d.ts +7 -7
  331. package/dist/pipeline/steps/browser.js +21 -7
  332. package/dist/pipeline/steps/fetch.d.ts +1 -1
  333. package/dist/pipeline/steps/fetch.js +11 -7
  334. package/dist/pipeline/steps/transform.d.ts +6 -5
  335. package/dist/pipeline/steps/transform.js +30 -9
  336. package/dist/pipeline/template.d.ts +6 -6
  337. package/dist/pipeline/template.js +43 -5
  338. package/dist/pipeline/template.test.js +18 -0
  339. package/dist/pipeline/transform.test.js +11 -0
  340. package/dist/plugin.d.ts +31 -0
  341. package/dist/plugin.js +216 -0
  342. package/dist/plugin.test.d.ts +4 -0
  343. package/dist/plugin.test.js +76 -0
  344. package/dist/registry-api.d.ts +11 -0
  345. package/dist/registry-api.js +9 -0
  346. package/dist/registry.d.ts +13 -0
  347. package/dist/registry.js +8 -1
  348. package/dist/runtime.d.ts +5 -0
  349. package/dist/runtime.js +8 -0
  350. package/dist/serialization.d.ts +34 -0
  351. package/dist/serialization.js +63 -0
  352. package/dist/synthesize.d.ts +94 -4
  353. package/dist/synthesize.js +5 -4
  354. package/dist/types.d.ts +43 -27
  355. package/dist/validate.js +8 -2
  356. package/docs/.vitepress/config.mts +20 -7
  357. package/docs/adapters/browser/arxiv.md +27 -0
  358. package/docs/adapters/browser/barchart.md +33 -0
  359. package/docs/adapters/browser/bilibili.md +9 -0
  360. package/docs/adapters/browser/bloomberg.md +70 -0
  361. package/docs/adapters/browser/chaoxing.md +39 -0
  362. package/docs/adapters/browser/devto.md +35 -0
  363. package/docs/adapters/browser/douban.md +38 -0
  364. package/docs/adapters/browser/facebook.md +36 -0
  365. package/docs/adapters/browser/google.md +62 -0
  366. package/docs/adapters/browser/grok.md +53 -0
  367. package/docs/adapters/browser/hf.md +42 -0
  368. package/docs/adapters/browser/instagram.md +46 -0
  369. package/docs/adapters/browser/jike.md +45 -0
  370. package/docs/adapters/browser/jimeng.md +39 -0
  371. package/docs/adapters/browser/linux-do.md +45 -0
  372. package/docs/adapters/browser/lobsters.md +32 -0
  373. package/docs/adapters/browser/medium.md +32 -0
  374. package/docs/adapters/browser/reddit.md +9 -0
  375. package/docs/adapters/browser/sinablog.md +36 -0
  376. package/docs/adapters/browser/sinafinance.md +35 -0
  377. package/docs/adapters/browser/stackoverflow.md +35 -0
  378. package/docs/adapters/browser/steam.md +26 -0
  379. package/docs/adapters/browser/substack.md +38 -0
  380. package/docs/adapters/browser/tiktok.md +68 -0
  381. package/docs/adapters/browser/twitter.md +3 -0
  382. package/docs/adapters/browser/weread.md +48 -0
  383. package/docs/adapters/browser/wikipedia.md +39 -0
  384. package/docs/adapters/browser/xiaohongshu.md +5 -1
  385. package/docs/adapters/browser/xueqiu.md +10 -0
  386. package/docs/adapters/browser/yahoo-finance.md +6 -5
  387. package/docs/adapters/desktop/antigravity.md +6 -0
  388. package/docs/adapters/desktop/chatgpt.md +5 -4
  389. package/docs/adapters/desktop/codex.md +5 -1
  390. package/docs/adapters/desktop/cursor.md +4 -0
  391. package/docs/adapters/desktop/discord.md +7 -7
  392. package/docs/adapters/index.md +14 -4
  393. package/docs/advanced/download.md +4 -4
  394. package/docs/developer/architecture.md +17 -4
  395. package/docs/guide/getting-started.md +1 -0
  396. package/docs/guide/plugins.md +153 -0
  397. package/docs/zh/guide/plugins.md +107 -0
  398. package/extension/src/background.ts +18 -11
  399. package/package.json +10 -5
  400. package/scripts/check-doc-coverage.sh +69 -0
  401. package/scripts/clean-dist.cjs +13 -0
  402. package/scripts/copy-yaml.cjs +7 -0
  403. package/src/browser/cdp.ts +77 -32
  404. package/src/browser/daemon-client.ts +2 -1
  405. package/src/browser/dom-helpers.ts +38 -7
  406. package/src/browser/dom-snapshot.test.ts +249 -0
  407. package/src/browser/dom-snapshot.ts +770 -0
  408. package/src/browser/index.ts +2 -0
  409. package/src/browser/page.ts +57 -20
  410. package/src/build-manifest.test.ts +70 -2
  411. package/src/build-manifest.ts +114 -40
  412. package/src/cli.ts +287 -139
  413. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  414. package/src/clis/apple-podcasts/search.ts +2 -2
  415. package/src/clis/apple-podcasts/top.ts +12 -2
  416. package/src/clis/arxiv/search.ts +1 -1
  417. package/src/clis/barchart/greeks.ts +1 -1
  418. package/src/clis/barchart/options.ts +1 -1
  419. package/src/clis/barchart/quote.ts +1 -1
  420. package/src/clis/bilibili/download.ts +1 -1
  421. package/src/clis/bilibili/dynamic.ts +1 -1
  422. package/src/clis/bilibili/favorite.ts +1 -1
  423. package/src/clis/bilibili/feed.ts +1 -1
  424. package/src/clis/bilibili/following.ts +2 -2
  425. package/src/clis/bilibili/history.ts +1 -1
  426. package/src/clis/bilibili/me.ts +1 -1
  427. package/src/clis/bilibili/ranking.ts +1 -1
  428. package/src/clis/bilibili/search.ts +3 -3
  429. package/src/clis/bilibili/subtitle.ts +2 -2
  430. package/src/clis/bilibili/user-videos.ts +2 -2
  431. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  432. package/src/clis/bloomberg/businessweek.ts +18 -0
  433. package/src/clis/bloomberg/economics.ts +18 -0
  434. package/src/clis/bloomberg/feeds.ts +16 -0
  435. package/src/clis/bloomberg/industries.ts +18 -0
  436. package/src/clis/bloomberg/main.ts +18 -0
  437. package/src/clis/bloomberg/markets.ts +18 -0
  438. package/src/clis/bloomberg/news.ts +136 -0
  439. package/src/clis/bloomberg/opinions.ts +18 -0
  440. package/src/clis/bloomberg/politics.ts +18 -0
  441. package/src/clis/bloomberg/tech.ts +18 -0
  442. package/src/clis/bloomberg/utils.test.ts +135 -0
  443. package/src/clis/bloomberg/utils.ts +429 -0
  444. package/src/clis/boss/batchgreet.ts +16 -108
  445. package/src/clis/boss/chatlist.ts +13 -27
  446. package/src/clis/boss/chatmsg.ts +16 -40
  447. package/src/clis/boss/common.ts +287 -0
  448. package/src/clis/boss/detail.ts +9 -55
  449. package/src/clis/boss/exchange.ts +15 -89
  450. package/src/clis/boss/greet.ts +25 -162
  451. package/src/clis/boss/invite.ts +36 -133
  452. package/src/clis/boss/joblist.ts +7 -36
  453. package/src/clis/boss/mark.ts +13 -94
  454. package/src/clis/boss/recommend.ts +12 -57
  455. package/src/clis/boss/resume.ts +19 -124
  456. package/src/clis/boss/search.ts +14 -67
  457. package/src/clis/boss/send.ts +22 -162
  458. package/src/clis/boss/stats.ts +21 -76
  459. package/src/clis/chaoxing/assignments.ts +1 -1
  460. package/src/clis/chaoxing/exams.ts +1 -1
  461. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  462. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  463. package/src/clis/chatgpt/read.ts +1 -1
  464. package/src/clis/chatwise/export.ts +1 -1
  465. package/src/clis/chatwise/model.ts +2 -2
  466. package/src/clis/chatwise/screenshot.ts +1 -1
  467. package/src/clis/codex/export.ts +1 -1
  468. package/src/clis/codex/model.ts +2 -2
  469. package/src/clis/codex/screenshot.ts +1 -1
  470. package/src/clis/coupang/add-to-cart.ts +3 -4
  471. package/src/clis/coupang/search.ts +2 -4
  472. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  473. package/src/clis/ctrip/search.ts +1 -1
  474. package/src/clis/cursor/export.ts +1 -1
  475. package/src/clis/cursor/model.ts +2 -2
  476. package/src/clis/cursor/screenshot.ts +1 -1
  477. package/src/clis/devto/tag.yaml +34 -0
  478. package/src/clis/devto/top.yaml +29 -0
  479. package/src/clis/devto/user.yaml +33 -0
  480. package/src/clis/douban/book-hot.ts +15 -0
  481. package/src/clis/douban/marks.ts +135 -0
  482. package/src/clis/douban/movie-hot.ts +15 -0
  483. package/src/clis/douban/reviews.ts +127 -0
  484. package/src/clis/douban/search.ts +17 -0
  485. package/src/clis/douban/shared.ts +165 -0
  486. package/src/clis/douban/subject.yaml +76 -0
  487. package/src/clis/douban/top250.yaml +70 -0
  488. package/src/clis/douban/utils.ts +81 -0
  489. package/src/clis/facebook/add-friend.yaml +43 -0
  490. package/src/clis/facebook/events.yaml +44 -0
  491. package/src/clis/facebook/feed.yaml +63 -0
  492. package/src/clis/facebook/friends.yaml +42 -0
  493. package/src/clis/facebook/groups.yaml +50 -0
  494. package/src/clis/facebook/join-group.yaml +44 -0
  495. package/src/clis/facebook/memories.yaml +39 -0
  496. package/src/clis/facebook/notifications.yaml +40 -0
  497. package/src/clis/facebook/profile.yaml +37 -0
  498. package/src/clis/facebook/search.yaml +46 -0
  499. package/src/clis/google/news.ts +66 -0
  500. package/src/clis/google/search.ts +133 -0
  501. package/src/clis/google/suggest.ts +40 -0
  502. package/src/clis/google/trends.ts +44 -0
  503. package/src/clis/google/utils.test.ts +82 -0
  504. package/src/clis/google/utils.ts +24 -0
  505. package/src/clis/grok/ask.test.ts +53 -0
  506. package/src/clis/grok/ask.ts +300 -69
  507. package/src/clis/instagram/comment.yaml +52 -0
  508. package/src/clis/instagram/explore.yaml +43 -0
  509. package/src/clis/instagram/follow.yaml +41 -0
  510. package/src/clis/instagram/followers.yaml +51 -0
  511. package/src/clis/instagram/following.yaml +51 -0
  512. package/src/clis/instagram/like.yaml +46 -0
  513. package/src/clis/instagram/profile.yaml +42 -0
  514. package/src/clis/instagram/save.yaml +46 -0
  515. package/src/clis/instagram/saved.yaml +40 -0
  516. package/src/clis/instagram/search.yaml +43 -0
  517. package/src/clis/instagram/unfollow.yaml +38 -0
  518. package/src/clis/instagram/unlike.yaml +46 -0
  519. package/src/clis/instagram/unsave.yaml +46 -0
  520. package/src/clis/instagram/user.yaml +54 -0
  521. package/src/clis/jike/comment.ts +2 -3
  522. package/src/clis/jike/create.ts +1 -2
  523. package/src/clis/jike/feed.ts +0 -1
  524. package/src/clis/jike/like.ts +1 -2
  525. package/src/clis/jike/notifications.ts +0 -1
  526. package/src/clis/jike/post.yaml +1 -0
  527. package/src/clis/jike/repost.ts +2 -3
  528. package/src/clis/jike/search.ts +2 -3
  529. package/src/clis/jike/topic.yaml +1 -0
  530. package/src/clis/jike/user.yaml +1 -0
  531. package/src/clis/jimeng/generate.yaml +1 -0
  532. package/src/clis/jimeng/history.yaml +0 -1
  533. package/src/clis/linkedin/search.ts +7 -7
  534. package/src/clis/linux-do/category.yaml +2 -0
  535. package/src/clis/linux-do/search.yaml +4 -3
  536. package/src/clis/linux-do/topic.yaml +1 -0
  537. package/src/clis/lobsters/active.yaml +29 -0
  538. package/src/clis/lobsters/hot.yaml +29 -0
  539. package/src/clis/lobsters/newest.yaml +29 -0
  540. package/src/clis/lobsters/tag.yaml +34 -0
  541. package/src/clis/medium/feed.ts +16 -0
  542. package/src/clis/medium/search.ts +16 -0
  543. package/src/clis/medium/shared.ts +83 -0
  544. package/src/clis/medium/user.ts +16 -0
  545. package/src/clis/notion/export.ts +1 -1
  546. package/src/clis/reddit/comment.ts +3 -4
  547. package/src/clis/reddit/read.ts +4 -5
  548. package/src/clis/reddit/save.ts +2 -3
  549. package/src/clis/reddit/saved.ts +0 -1
  550. package/src/clis/reddit/search.yaml +1 -0
  551. package/src/clis/reddit/subreddit.yaml +1 -0
  552. package/src/clis/reddit/subscribe.ts +1 -2
  553. package/src/clis/reddit/upvote.ts +2 -3
  554. package/src/clis/reddit/upvoted.ts +0 -1
  555. package/src/clis/reddit/user-comments.yaml +1 -0
  556. package/src/clis/reddit/user-posts.yaml +1 -0
  557. package/src/clis/reddit/user.yaml +1 -0
  558. package/src/clis/reuters/search.ts +1 -1
  559. package/src/clis/sinablog/article.ts +15 -0
  560. package/src/clis/sinablog/hot.ts +15 -0
  561. package/src/clis/sinablog/search.ts +56 -0
  562. package/src/clis/sinablog/shared.ts +198 -0
  563. package/src/clis/sinablog/user.ts +16 -0
  564. package/src/clis/smzdm/search.ts +2 -3
  565. package/src/clis/stackoverflow/search.yaml +1 -0
  566. package/src/clis/steam/top-sellers.yaml +29 -0
  567. package/src/clis/substack/feed.ts +16 -0
  568. package/src/clis/substack/publication.ts +16 -0
  569. package/src/clis/substack/search.ts +91 -0
  570. package/src/clis/substack/shared.ts +132 -0
  571. package/src/clis/tiktok/comment.yaml +66 -0
  572. package/src/clis/tiktok/explore.yaml +39 -0
  573. package/src/clis/tiktok/follow.yaml +39 -0
  574. package/src/clis/tiktok/following.yaml +46 -0
  575. package/src/clis/tiktok/friends.yaml +47 -0
  576. package/src/clis/tiktok/like.yaml +38 -0
  577. package/src/clis/tiktok/live.yaml +51 -0
  578. package/src/clis/tiktok/notifications.yaml +52 -0
  579. package/src/clis/tiktok/profile.yaml +45 -0
  580. package/src/clis/tiktok/save.yaml +34 -0
  581. package/src/clis/tiktok/search.yaml +46 -0
  582. package/src/clis/tiktok/unfollow.yaml +44 -0
  583. package/src/clis/tiktok/unlike.yaml +38 -0
  584. package/src/clis/tiktok/unsave.yaml +36 -0
  585. package/src/clis/tiktok/user.yaml +44 -0
  586. package/src/clis/twitter/accept.ts +2 -2
  587. package/src/clis/twitter/article.ts +2 -2
  588. package/src/clis/twitter/block.ts +92 -0
  589. package/src/clis/twitter/delete.ts +1 -1
  590. package/src/clis/twitter/download.ts +3 -3
  591. package/src/clis/twitter/followers.ts +1 -1
  592. package/src/clis/twitter/following.ts +1 -1
  593. package/src/clis/twitter/hide-reply.ts +70 -0
  594. package/src/clis/twitter/like.ts +1 -1
  595. package/src/clis/twitter/post.ts +1 -1
  596. package/src/clis/twitter/reply-dm.ts +1 -1
  597. package/src/clis/twitter/reply.ts +2 -2
  598. package/src/clis/twitter/search.ts +1 -1
  599. package/src/clis/twitter/thread.ts +2 -2
  600. package/src/clis/twitter/timeline.test.ts +109 -0
  601. package/src/clis/twitter/timeline.ts +59 -19
  602. package/src/clis/twitter/trending.ts +113 -0
  603. package/src/clis/twitter/unblock.ts +75 -0
  604. package/src/clis/v2ex/topic.yaml +1 -0
  605. package/src/clis/weibo/hot.ts +0 -1
  606. package/src/clis/weread/book.ts +1 -1
  607. package/src/clis/weread/highlights.ts +1 -1
  608. package/src/clis/weread/notes.ts +1 -1
  609. package/src/clis/weread/search.ts +1 -1
  610. package/src/clis/wikipedia/random.ts +19 -0
  611. package/src/clis/wikipedia/search.ts +11 -5
  612. package/src/clis/wikipedia/summary.ts +4 -9
  613. package/src/clis/wikipedia/trending.ts +41 -0
  614. package/src/clis/wikipedia/utils.ts +31 -0
  615. package/src/clis/xiaohongshu/creator-note-detail.test.ts +84 -33
  616. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  617. package/src/clis/xiaohongshu/creator-notes.test.ts +41 -6
  618. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  619. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  620. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  621. package/src/clis/xiaohongshu/download.ts +2 -3
  622. package/src/clis/xiaohongshu/feed.yaml +0 -1
  623. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  624. package/src/clis/xiaohongshu/search.ts +2 -2
  625. package/src/clis/xiaohongshu/user.ts +1 -2
  626. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  627. package/src/clis/xueqiu/search.yaml +2 -1
  628. package/src/clis/xueqiu/stock.yaml +2 -0
  629. package/src/clis/yahoo-finance/quote.ts +1 -2
  630. package/src/clis/youtube/search.ts +1 -1
  631. package/src/clis/youtube/transcript.ts +1 -1
  632. package/src/clis/youtube/video.ts +1 -1
  633. package/src/clis/zhihu/download.ts +1 -2
  634. package/src/clis/zhihu/question.ts +1 -1
  635. package/src/clis/zhihu/search.yaml +4 -3
  636. package/src/commanderAdapter.ts +120 -0
  637. package/src/discovery.ts +277 -0
  638. package/src/doctor.test.ts +59 -2
  639. package/src/doctor.ts +4 -2
  640. package/src/download/index.ts +21 -54
  641. package/src/engine.test.ts +85 -11
  642. package/src/execution.ts +164 -0
  643. package/src/explore.ts +211 -117
  644. package/src/external-clis.yaml +9 -0
  645. package/src/external.ts +15 -12
  646. package/src/generate.ts +58 -9
  647. package/src/main.ts +2 -1
  648. package/src/pipeline/executor.test.ts +35 -6
  649. package/src/pipeline/executor.ts +11 -7
  650. package/src/pipeline/registry.ts +3 -3
  651. package/src/pipeline/steps/browser.ts +29 -15
  652. package/src/pipeline/steps/fetch.ts +18 -13
  653. package/src/pipeline/steps/transform.ts +40 -15
  654. package/src/pipeline/template.test.ts +18 -0
  655. package/src/pipeline/template.ts +86 -13
  656. package/src/pipeline/transform.test.ts +15 -2
  657. package/src/plugin.test.ts +86 -0
  658. package/src/plugin.ts +254 -0
  659. package/src/registry-api.ts +12 -0
  660. package/src/registry.ts +24 -1
  661. package/src/runtime.ts +9 -0
  662. package/src/serialization.ts +79 -0
  663. package/src/synthesize.ts +102 -21
  664. package/src/types.ts +45 -13
  665. package/src/validate.ts +19 -4
  666. package/tests/e2e/browser-public.test.ts +36 -0
  667. package/tests/e2e/public-commands.test.ts +119 -1
  668. package/dist/clis/feishu/new.d.ts +0 -1
  669. package/dist/clis/feishu/new.js +0 -27
  670. package/dist/clis/feishu/read.d.ts +0 -1
  671. package/dist/clis/feishu/read.js +0 -40
  672. package/dist/clis/feishu/search.d.ts +0 -1
  673. package/dist/clis/feishu/search.js +0 -30
  674. package/dist/clis/feishu/send.d.ts +0 -1
  675. package/dist/clis/feishu/send.js +0 -39
  676. package/dist/clis/feishu/status.d.ts +0 -1
  677. package/dist/clis/feishu/status.js +0 -28
  678. package/dist/clis/neteasemusic/like.d.ts +0 -1
  679. package/dist/clis/neteasemusic/like.js +0 -25
  680. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  681. package/dist/clis/neteasemusic/lyrics.js +0 -47
  682. package/dist/clis/neteasemusic/next.d.ts +0 -1
  683. package/dist/clis/neteasemusic/next.js +0 -26
  684. package/dist/clis/neteasemusic/play.d.ts +0 -1
  685. package/dist/clis/neteasemusic/play.js +0 -26
  686. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  687. package/dist/clis/neteasemusic/playing.js +0 -59
  688. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  689. package/dist/clis/neteasemusic/playlist.js +0 -46
  690. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  691. package/dist/clis/neteasemusic/prev.js +0 -25
  692. package/dist/clis/neteasemusic/search.d.ts +0 -1
  693. package/dist/clis/neteasemusic/search.js +0 -52
  694. package/dist/clis/neteasemusic/status.d.ts +0 -1
  695. package/dist/clis/neteasemusic/status.js +0 -16
  696. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  697. package/dist/clis/neteasemusic/volume.js +0 -54
  698. package/dist/clis/twitter/trending.yaml +0 -46
  699. package/dist/clis/wechat/chats.d.ts +0 -1
  700. package/dist/clis/wechat/chats.js +0 -28
  701. package/dist/clis/wechat/contacts.d.ts +0 -1
  702. package/dist/clis/wechat/contacts.js +0 -28
  703. package/dist/clis/wechat/read.d.ts +0 -1
  704. package/dist/clis/wechat/read.js +0 -58
  705. package/dist/clis/wechat/search.d.ts +0 -1
  706. package/dist/clis/wechat/search.js +0 -31
  707. package/dist/clis/wechat/send.d.ts +0 -1
  708. package/dist/clis/wechat/send.js +0 -42
  709. package/dist/clis/wechat/status.d.ts +0 -1
  710. package/dist/clis/wechat/status.js +0 -29
  711. package/dist/pipeline.d.ts +0 -7
  712. package/dist/pipeline.js +0 -7
  713. package/docs/adapters/browser/github.md +0 -26
  714. package/docs/adapters/desktop/feishu.md +0 -20
  715. package/docs/adapters/desktop/neteasemusic.md +0 -31
  716. package/docs/adapters/desktop/wechat.md +0 -28
  717. package/docs/public/CNAME +0 -1
  718. package/src/clis/antigravity/README.md +0 -5
  719. package/src/clis/antigravity/README.zh-CN.md +0 -51
  720. package/src/clis/chaoxing/README.md +0 -14
  721. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  722. package/src/clis/chatgpt/README.md +0 -5
  723. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  724. package/src/clis/chatwise/README.md +0 -5
  725. package/src/clis/chatwise/README.zh-CN.md +0 -38
  726. package/src/clis/codex/README.md +0 -5
  727. package/src/clis/codex/README.zh-CN.md +0 -33
  728. package/src/clis/cursor/README.md +0 -5
  729. package/src/clis/cursor/README.zh-CN.md +0 -33
  730. package/src/clis/discord-app/README.md +0 -5
  731. package/src/clis/discord-app/README.zh-CN.md +0 -28
  732. package/src/clis/feishu/README.md +0 -5
  733. package/src/clis/feishu/README.zh-CN.md +0 -20
  734. package/src/clis/feishu/new.ts +0 -32
  735. package/src/clis/feishu/read.ts +0 -48
  736. package/src/clis/feishu/search.ts +0 -35
  737. package/src/clis/feishu/send.ts +0 -46
  738. package/src/clis/feishu/status.ts +0 -34
  739. package/src/clis/neteasemusic/README.md +0 -5
  740. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  741. package/src/clis/neteasemusic/like.ts +0 -28
  742. package/src/clis/neteasemusic/lyrics.ts +0 -53
  743. package/src/clis/neteasemusic/next.ts +0 -30
  744. package/src/clis/neteasemusic/play.ts +0 -30
  745. package/src/clis/neteasemusic/playing.ts +0 -62
  746. package/src/clis/neteasemusic/playlist.ts +0 -51
  747. package/src/clis/neteasemusic/prev.ts +0 -29
  748. package/src/clis/neteasemusic/search.ts +0 -58
  749. package/src/clis/neteasemusic/status.ts +0 -18
  750. package/src/clis/neteasemusic/volume.ts +0 -61
  751. package/src/clis/notion/README.md +0 -5
  752. package/src/clis/notion/README.zh-CN.md +0 -29
  753. package/src/clis/twitter/trending.yaml +0 -46
  754. package/src/clis/wechat/README.md +0 -5
  755. package/src/clis/wechat/README.zh-CN.md +0 -28
  756. package/src/clis/wechat/chats.ts +0 -33
  757. package/src/clis/wechat/contacts.ts +0 -33
  758. package/src/clis/wechat/read.ts +0 -72
  759. package/src/clis/wechat/search.ts +0 -36
  760. package/src/clis/wechat/send.ts +0 -49
  761. package/src/clis/wechat/status.ts +0 -35
  762. package/src/engine.ts +0 -274
  763. package/src/pipeline.ts +0 -8
  764. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  765. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  766. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  767. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  768. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  769. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -2,7 +2,7 @@
2
2
  * Twitter/X download — download images and videos from tweets.
3
3
  *
4
4
  * Usage:
5
- * opencli twitter download --username elonmusk --limit 10 --output ./twitter
5
+ * opencli twitter download elonmusk --limit 10 --output ./twitter
6
6
  * opencli twitter download --tweet-url https://x.com/xxx/status/123 --output ./twitter
7
7
  */
8
8
 
@@ -27,7 +27,7 @@ cli({
27
27
  domain: 'x.com',
28
28
  strategy: Strategy.COOKIE,
29
29
  args: [
30
- { name: 'username', help: 'Twitter username (downloads from media tab)' },
30
+ { name: 'username', positional: true, help: 'Twitter username (downloads from media tab)' },
31
31
  { name: 'tweet-url', help: 'Single tweet URL to download' },
32
32
  { name: 'limit', type: 'int', default: 10, help: 'Number of tweets to scan' },
33
33
  { name: 'output', default: './twitter-downloads', help: 'Output directory' },
@@ -44,7 +44,7 @@ cli({
44
44
  index: 0,
45
45
  type: '-',
46
46
  status: 'failed',
47
- size: 'Must provide --username or --tweet-url',
47
+ size: 'Must provide a username or --tweet-url',
48
48
  }];
49
49
  }
50
50
 
@@ -8,7 +8,7 @@ cli({
8
8
  strategy: Strategy.INTERCEPT,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'user', type: 'string', required: false },
11
+ { name: 'user', positional: true, type: 'string', required: false },
12
12
  { name: 'limit', type: 'int', default: 50 },
13
13
  ],
14
14
  columns: ['screen_name', 'name', 'bio', 'followers'],
@@ -8,7 +8,7 @@ cli({
8
8
  strategy: Strategy.INTERCEPT,
9
9
  browser: true,
10
10
  args: [
11
- { name: 'user', type: 'string', required: false },
11
+ { name: 'user', positional: true, type: 'string', required: false },
12
12
  { name: 'limit', type: 'int', default: 50 },
13
13
  ],
14
14
  columns: ['screen_name', 'name', 'bio', 'followers'],
@@ -0,0 +1,70 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import type { IPage } from '../../types.js';
3
+
4
+ cli({
5
+ site: 'twitter',
6
+ name: 'hide-reply',
7
+ description: 'Hide a reply on your tweet (useful for hiding bot/spam replies)',
8
+ domain: 'x.com',
9
+ strategy: Strategy.UI,
10
+ browser: true,
11
+ args: [
12
+ { name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the reply tweet to hide' },
13
+ ],
14
+ columns: ['status', 'message'],
15
+ func: async (page: IPage | null, kwargs: any) => {
16
+ if (!page) throw new Error('Requires browser');
17
+
18
+ await page.goto(kwargs.url);
19
+ await page.wait(5);
20
+
21
+ const result = await page.evaluate(`(async () => {
22
+ try {
23
+ let attempts = 0;
24
+ let moreMenu = null;
25
+
26
+ while (attempts < 20) {
27
+ moreMenu = document.querySelector('[aria-label="More"]');
28
+ if (moreMenu) break;
29
+ await new Promise(r => setTimeout(r, 500));
30
+ attempts++;
31
+ }
32
+
33
+ if (!moreMenu) {
34
+ return { ok: false, message: 'Could not find the "More" menu on this tweet. Are you logged in?' };
35
+ }
36
+
37
+ moreMenu.click();
38
+ await new Promise(r => setTimeout(r, 1000));
39
+
40
+ // Look for the "Hide reply" menu item
41
+ const items = document.querySelectorAll('[role="menuitem"]');
42
+ let hideItem = null;
43
+ for (const item of items) {
44
+ if (item.textContent && item.textContent.includes('Hide reply')) {
45
+ hideItem = item;
46
+ break;
47
+ }
48
+ }
49
+
50
+ if (!hideItem) {
51
+ return { ok: false, message: 'Could not find "Hide reply" option. This may not be a reply on your tweet.' };
52
+ }
53
+
54
+ hideItem.click();
55
+ await new Promise(r => setTimeout(r, 1500));
56
+
57
+ return { ok: true, message: 'Reply successfully hidden.' };
58
+ } catch (e) {
59
+ return { ok: false, message: e.toString() };
60
+ }
61
+ })()`);
62
+
63
+ if (result.ok) await page.wait(2);
64
+
65
+ return [{
66
+ status: result.ok ? 'success' : 'failed',
67
+ message: result.message
68
+ }];
69
+ }
70
+ });
@@ -9,7 +9,7 @@ cli({
9
9
  strategy: Strategy.UI, // Utilizes internal DOM flows for interaction
10
10
  browser: true,
11
11
  args: [
12
- { name: 'url', type: 'string', required: true, help: 'The URL of the tweet to like' },
12
+ { name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to like' },
13
13
  ],
14
14
  columns: ['status', 'message'],
15
15
  func: async (page: IPage | null, kwargs: any) => {
@@ -9,7 +9,7 @@ cli({
9
9
  strategy: Strategy.UI,
10
10
  browser: true,
11
11
  args: [
12
- { name: 'text', type: 'string', required: true, help: 'The text content of the tweet' },
12
+ { name: 'text', type: 'string', required: true, positional: true, help: 'The text content of the tweet' },
13
13
  ],
14
14
  columns: ['status', 'message', 'text'],
15
15
  func: async (page: IPage | null, kwargs: any) => {
@@ -10,7 +10,7 @@ cli({
10
10
  browser: true,
11
11
  timeoutSeconds: 600, // 10 min — batch operation
12
12
  args: [
13
- { name: 'text', type: 'string', required: true, help: 'Message text to send (e.g. "我的微信 wxkabi")' },
13
+ { name: 'text', type: 'string', required: true, positional: true, help: 'Message text to send (e.g. "我的微信 wxkabi")' },
14
14
  { name: 'max', type: 'int', required: false, default: 20, help: 'Maximum number of conversations to reply to (default: 20)' },
15
15
  { name: 'skip-replied', type: 'boolean', required: false, default: true, help: 'Skip conversations where you already sent the same text (default: true)' },
16
16
  ],
@@ -9,8 +9,8 @@ cli({
9
9
  strategy: Strategy.UI, // Uses the UI directly to input and click post
10
10
  browser: true,
11
11
  args: [
12
- { name: 'url', type: 'string', required: true, help: 'The URL of the tweet to reply to' },
13
- { name: 'text', type: 'string', required: true, help: 'The text content of your reply' },
12
+ { name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to reply to' },
13
+ { name: 'text', type: 'string', required: true, positional: true, help: 'The text content of your reply' },
14
14
  ],
15
15
  columns: ['status', 'message', 'text'],
16
16
  func: async (page: IPage | null, kwargs: any) => {
@@ -8,7 +8,7 @@ cli({
8
8
  strategy: Strategy.INTERCEPT, // Use intercept strategy
9
9
  browser: true,
10
10
  args: [
11
- { name: 'query', type: 'string', required: true },
11
+ { name: 'query', type: 'string', required: true, positional: true },
12
12
  { name: 'limit', type: 'int', default: 15 },
13
13
  ],
14
14
  columns: ['id', 'author', 'text', 'likes', 'views', 'url'],
@@ -122,12 +122,12 @@ cli({
122
122
  strategy: Strategy.COOKIE,
123
123
  browser: true,
124
124
  args: [
125
- { name: 'tweet_id', type: 'string', required: true },
125
+ { name: 'tweet-id', positional: true, type: 'string', required: true },
126
126
  { name: 'limit', type: 'int', default: 50 },
127
127
  ],
128
128
  columns: ['id', 'author', 'text', 'likes', 'retweets', 'url'],
129
129
  func: async (page, kwargs) => {
130
- let tweetId = kwargs.tweet_id;
130
+ let tweetId = kwargs['tweet-id'];
131
131
  const urlMatch = tweetId.match(/\/status\/(\d+)/);
132
132
  if (urlMatch) tweetId = urlMatch[1];
133
133
 
@@ -0,0 +1,109 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { __test__ } from './timeline.js';
3
+
4
+ describe('twitter timeline helpers', () => {
5
+ it('builds for-you variables with withCommunity', () => {
6
+ expect(__test__.buildTimelineVariables('for-you', 20)).toEqual({
7
+ count: 20,
8
+ includePromotedContent: false,
9
+ latestControlAvailable: true,
10
+ requestContext: 'launch',
11
+ withCommunity: true,
12
+ });
13
+ });
14
+
15
+ it('builds following variables with seenTweetIds instead of withCommunity', () => {
16
+ expect(__test__.buildTimelineVariables('following', 20, 'cursor-1')).toEqual({
17
+ count: 20,
18
+ includePromotedContent: false,
19
+ latestControlAvailable: true,
20
+ requestContext: 'launch',
21
+ seenTweetIds: [],
22
+ cursor: 'cursor-1',
23
+ });
24
+ });
25
+
26
+ it('encodes variables into timeline url', () => {
27
+ const url = __test__.buildHomeTimelineUrl('query123', 'HomeLatestTimeline', {
28
+ count: 20,
29
+ seenTweetIds: [],
30
+ });
31
+
32
+ expect(url).toContain('/i/api/graphql/query123/HomeLatestTimeline');
33
+ expect(url).toContain('variables=');
34
+ expect(url).toContain('features=');
35
+ expect(decodeURIComponent(url)).toContain('"seenTweetIds":[]');
36
+ });
37
+
38
+ it('parses tweets and bottom cursor from home timeline payload', () => {
39
+ const payload = {
40
+ data: {
41
+ home: {
42
+ home_timeline_urt: {
43
+ instructions: [
44
+ {
45
+ entries: [
46
+ {
47
+ entryId: 'tweet-1',
48
+ content: {
49
+ itemContent: {
50
+ tweet_results: {
51
+ result: {
52
+ rest_id: '1',
53
+ legacy: {
54
+ full_text: 'hello',
55
+ favorite_count: 3,
56
+ retweet_count: 2,
57
+ reply_count: 1,
58
+ created_at: 'now',
59
+ },
60
+ core: {
61
+ user_results: {
62
+ result: {
63
+ legacy: {
64
+ screen_name: 'alice',
65
+ },
66
+ },
67
+ },
68
+ },
69
+ views: {
70
+ count: '9',
71
+ },
72
+ },
73
+ },
74
+ },
75
+ },
76
+ },
77
+ {
78
+ entryId: 'cursor-bottom-1',
79
+ content: {
80
+ entryType: 'TimelineTimelineCursor',
81
+ cursorType: 'Bottom',
82
+ value: 'cursor-next',
83
+ },
84
+ },
85
+ ],
86
+ },
87
+ ],
88
+ },
89
+ },
90
+ },
91
+ };
92
+
93
+ const result = __test__.parseHomeTimeline(payload, new Set());
94
+
95
+ expect(result.nextCursor).toBe('cursor-next');
96
+ expect(result.tweets).toHaveLength(1);
97
+ expect(result.tweets[0]).toMatchObject({
98
+ id: '1',
99
+ author: 'alice',
100
+ text: 'hello',
101
+ likes: 3,
102
+ retweets: 2,
103
+ replies: 1,
104
+ views: 9,
105
+ created_at: 'now',
106
+ url: 'https://x.com/alice/status/1',
107
+ });
108
+ });
109
+ });
@@ -2,8 +2,24 @@ import { cli, Strategy } from '../../registry.js';
2
2
 
3
3
  // ── Twitter GraphQL constants ──────────────────────────────────────────
4
4
 
5
- const BEARER_TOKEN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
5
+ const BEARER_TOKEN =
6
+ 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
6
7
  const HOME_TIMELINE_QUERY_ID = 'c-CzHF1LboFilMpsx4ZCrQ';
8
+ const HOME_LATEST_TIMELINE_QUERY_ID = 'BKB7oi212Fi7kQtCBGE4zA';
9
+
10
+ type TimelineType = 'for-you' | 'following';
11
+
12
+ interface TimelineEndpointConfig {
13
+ endpoint: string;
14
+ method: 'GET' | 'POST';
15
+ fallbackQueryId: string;
16
+ }
17
+
18
+ // Endpoint config: for-you uses GET HomeTimeline, following uses POST HomeLatestTimeline
19
+ const TIMELINE_ENDPOINTS: Record<TimelineType, TimelineEndpointConfig> = {
20
+ 'for-you': { endpoint: 'HomeTimeline', method: 'GET', fallbackQueryId: HOME_TIMELINE_QUERY_ID },
21
+ following: { endpoint: 'HomeLatestTimeline', method: 'POST', fallbackQueryId: HOME_LATEST_TIMELINE_QUERY_ID },
22
+ };
7
23
 
8
24
  const FEATURES = {
9
25
  rweb_video_screen_enabled: false,
@@ -53,19 +69,26 @@ interface TimelineTweet {
53
69
  url: string;
54
70
  }
55
71
 
56
- function buildHomeTimelineUrl(count: number, cursor?: string | null): string {
57
- const vars: Record<string, any> = {
72
+ function buildTimelineVariables(type: TimelineType, count: number, cursor?: string | null): Record<string, unknown> {
73
+ const vars: Record<string, unknown> = {
58
74
  count,
59
75
  includePromotedContent: false,
60
76
  latestControlAvailable: true,
61
77
  requestContext: 'launch',
62
- withCommunity: true,
63
78
  };
79
+ if (type === 'for-you') vars.withCommunity = true;
80
+ if (type === 'following') vars.seenTweetIds = [];
64
81
  if (cursor) vars.cursor = cursor;
82
+ return vars;
83
+ }
84
+
85
+ function buildHomeTimelineUrl(queryId: string, endpoint: string, vars: Record<string, unknown>): string {
65
86
 
66
- return `/i/api/graphql/${HOME_TIMELINE_QUERY_ID}/HomeTimeline`
67
- + `?variables=${encodeURIComponent(JSON.stringify(vars))}`
68
- + `&features=${encodeURIComponent(JSON.stringify(FEATURES))}`;
87
+ return (
88
+ `/i/api/graphql/${queryId}/${endpoint}` +
89
+ `?variables=${encodeURIComponent(JSON.stringify(vars))}` +
90
+ `&features=${encodeURIComponent(JSON.stringify(FEATURES))}`
91
+ );
69
92
  }
70
93
 
71
94
  function extractTweet(result: any, seen: Set<string>): TimelineTweet | null {
@@ -97,8 +120,8 @@ function parseHomeTimeline(data: any, seen: Set<string>): { tweets: TimelineTwee
97
120
  const tweets: TimelineTweet[] = [];
98
121
  let nextCursor: string | null = null;
99
122
 
100
- const instructions =
101
- data?.data?.home?.home_timeline_urt?.instructions || [];
123
+ // Both HomeTimeline and HomeLatestTimeline share the same response envelope
124
+ const instructions = data?.data?.home?.home_timeline_urt?.instructions || [];
102
125
 
103
126
  for (const inst of instructions) {
104
127
  for (const entry of inst.entries || []) {
@@ -144,16 +167,24 @@ function parseHomeTimeline(data: any, seen: Set<string>): { tweets: TimelineTwee
144
167
  cli({
145
168
  site: 'twitter',
146
169
  name: 'timeline',
147
- description: 'Fetch Twitter Home Timeline',
170
+ description: 'Fetch Twitter timeline (for-you or following)',
148
171
  domain: 'x.com',
149
172
  strategy: Strategy.COOKIE,
150
173
  browser: true,
151
174
  args: [
175
+ {
176
+ name: 'type',
177
+ default: 'for-you',
178
+ choices: ['for-you', 'following'],
179
+ help: 'Timeline type: for-you (algorithmic) or following (chronological)',
180
+ },
152
181
  { name: 'limit', type: 'int', default: 20 },
153
182
  ],
154
183
  columns: ['id', 'author', 'text', 'likes', 'retweets', 'replies', 'views', 'created_at', 'url'],
155
184
  func: async (page, kwargs) => {
156
185
  const limit = kwargs.limit || 20;
186
+ const timelineType: TimelineType = kwargs.type === 'following' ? 'following' : 'for-you';
187
+ const { endpoint, method, fallbackQueryId } = TIMELINE_ENDPOINTS[timelineType];
157
188
 
158
189
  // Navigate to x.com for cookie context
159
190
  await page.goto('https://x.com');
@@ -165,22 +196,24 @@ cli({
165
196
  }`);
166
197
  if (!ct0) throw new Error('Not logged into x.com (no ct0 cookie)');
167
198
 
168
- // Dynamically resolve queryId
169
- const queryId = await page.evaluate(`async () => {
199
+ // Dynamically resolve queryId for the selected endpoint
200
+ const resolved = await page.evaluate(`async () => {
170
201
  try {
171
202
  const ghResp = await fetch('https://raw.githubusercontent.com/fa0311/twitter-openapi/refs/heads/main/src/config/placeholder.json');
172
203
  if (ghResp.ok) {
173
204
  const data = await ghResp.json();
174
- const entry = data['HomeTimeline'];
205
+ const entry = data['${endpoint}'];
175
206
  if (entry && entry.queryId) return entry.queryId;
176
207
  }
177
208
  } catch {}
178
209
  return null;
179
- }`) || HOME_TIMELINE_QUERY_ID;
210
+ }`);
211
+ // Validate queryId format to prevent injection from untrusted upstream
212
+ const queryId = typeof resolved === 'string' && /^[A-Za-z0-9_-]+$/.test(resolved) ? resolved : fallbackQueryId;
180
213
 
181
214
  // Build auth headers
182
215
  const headers = JSON.stringify({
183
- 'Authorization': `Bearer ${decodeURIComponent(BEARER_TOKEN)}`,
216
+ Authorization: `Bearer ${decodeURIComponent(BEARER_TOKEN)}`,
184
217
  'X-Csrf-Token': ct0,
185
218
  'X-Twitter-Auth-Type': 'OAuth2Session',
186
219
  'X-Twitter-Active-User': 'yes',
@@ -193,16 +226,17 @@ cli({
193
226
 
194
227
  for (let i = 0; i < 5 && allTweets.length < limit; i++) {
195
228
  const fetchCount = Math.min(40, limit - allTweets.length + 5); // over-fetch slightly for promoted filtering
196
- const apiUrl = buildHomeTimelineUrl(fetchCount, cursor)
197
- .replace(HOME_TIMELINE_QUERY_ID, queryId);
229
+ const variables = buildTimelineVariables(timelineType, fetchCount, cursor);
230
+ const apiUrl = buildHomeTimelineUrl(queryId, endpoint, variables);
198
231
 
199
232
  const data = await page.evaluate(`async () => {
200
- const r = await fetch("${apiUrl}", { headers: ${headers}, credentials: 'include' });
233
+ const r = await fetch("${apiUrl}", { method: "${method}", headers: ${headers}, credentials: 'include' });
201
234
  return r.ok ? await r.json() : { error: r.status };
202
235
  }`);
203
236
 
204
237
  if (data?.error) {
205
- if (allTweets.length === 0) throw new Error(`HTTP ${data.error}: Failed to fetch timeline. queryId may have expired.`);
238
+ if (allTweets.length === 0)
239
+ throw new Error(`HTTP ${data.error}: Failed to fetch timeline. queryId may have expired.`);
206
240
  break;
207
241
  }
208
242
 
@@ -216,3 +250,9 @@ cli({
216
250
  return allTweets.slice(0, limit);
217
251
  },
218
252
  });
253
+
254
+ export const __test__ = {
255
+ buildTimelineVariables,
256
+ buildHomeTimelineUrl,
257
+ parseHomeTimeline,
258
+ };
@@ -0,0 +1,113 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+
3
+ // ── Twitter GraphQL constants ──────────────────────────────────────────
4
+
5
+ const BEARER_TOKEN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
6
+
7
+ // ── Types ──────────────────────────────────────────────────────────────
8
+
9
+ interface TrendItem {
10
+ rank: number;
11
+ topic: string;
12
+ tweets: string;
13
+ category: string;
14
+ }
15
+
16
+ // ── CLI definition ────────────────────────────────────────────────────
17
+
18
+ cli({
19
+ site: 'twitter',
20
+ name: 'trending',
21
+ description: 'Twitter/X trending topics',
22
+ domain: 'x.com',
23
+ strategy: Strategy.COOKIE,
24
+ browser: true,
25
+ args: [
26
+ { name: 'limit', type: 'int', default: 20, help: 'Number of trends to show' },
27
+ ],
28
+ columns: ['rank', 'topic', 'tweets', 'category'],
29
+ func: async (page, kwargs) => {
30
+ const limit = kwargs.limit || 20;
31
+
32
+ // Navigate to trending page
33
+ await page.goto('https://x.com/explore/tabs/trending');
34
+ await page.wait(3);
35
+
36
+ // Extract CSRF token to verify login
37
+ const ct0 = await page.evaluate(`(() => {
38
+ return document.cookie.split(';').map(c=>c.trim()).find(c=>c.startsWith('ct0='))?.split('=')[1] || null;
39
+ })()`);
40
+ if (!ct0) throw new Error('Not logged into x.com (no ct0 cookie)');
41
+
42
+ // Try legacy guide.json API first (faster than DOM scraping)
43
+ let trends: TrendItem[] = [];
44
+
45
+ const apiData = await page.evaluate(`(async () => {
46
+ const ct0 = document.cookie.split(';').map(c=>c.trim()).find(c=>c.startsWith('ct0='))?.split('=')[1] || '';
47
+ const r = await fetch('/i/api/2/guide.json?include_page_configuration=true', {
48
+ credentials: 'include',
49
+ headers: {
50
+ 'x-twitter-active-user': 'yes',
51
+ 'x-csrf-token': ct0,
52
+ 'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA'
53
+ }
54
+ });
55
+ return r.ok ? await r.json() : null;
56
+ })()`);
57
+
58
+ if (apiData) {
59
+ const instructions = apiData?.timeline?.instructions || [];
60
+ const entries = instructions.flatMap((inst: any) => inst?.addEntries?.entries || inst?.entries || []);
61
+ const apiTrends = entries
62
+ .filter((e: any) => e.content?.timelineModule)
63
+ .flatMap((e: any) => e.content.timelineModule.items || [])
64
+ .map((t: any) => t?.item?.content?.trend)
65
+ .filter(Boolean);
66
+
67
+ trends = apiTrends.map((t: any, i: number) => ({
68
+ rank: i + 1,
69
+ topic: t.name,
70
+ tweets: t.tweetCount ? String(t.tweetCount) : 'N/A',
71
+ category: t.trendMetadata?.domainContext || '',
72
+ }));
73
+ }
74
+
75
+ // Fallback: scrape from the loaded DOM
76
+ if (trends.length === 0) {
77
+ await page.wait(2);
78
+ const domTrends = await page.evaluate(`(() => {
79
+ const items = [];
80
+ const cells = document.querySelectorAll('[data-testid="trend"]');
81
+ cells.forEach((cell) => {
82
+ const text = cell.textContent || '';
83
+ if (text.includes('Promoted')) return;
84
+ const container = cell.querySelector(':scope > div');
85
+ if (!container) return;
86
+ const divs = container.children;
87
+ // Structure: divs[0] = rank + category, divs[1] = topic name, divs[2] = extra
88
+ const topicEl = divs.length >= 2 ? divs[1] : null;
89
+ const topic = topicEl ? topicEl.textContent.trim() : '';
90
+ const catEl = divs.length >= 1 ? divs[0] : null;
91
+ const catText = catEl ? catEl.textContent.trim() : '';
92
+ const category = catText.replace(/^\\d+\\s*/, '').replace(/^\\xB7\\s*/, '').trim();
93
+ const extraEl = divs.length >= 3 ? divs[2] : null;
94
+ const extra = extraEl ? extraEl.textContent.trim() : '';
95
+ if (topic) {
96
+ items.push({ rank: items.length + 1, topic, tweets: extra || 'N/A', category });
97
+ }
98
+ });
99
+ return items;
100
+ })()`);
101
+
102
+ if (Array.isArray(domTrends) && domTrends.length > 0) {
103
+ trends = domTrends;
104
+ }
105
+ }
106
+
107
+ if (trends.length === 0) {
108
+ throw new Error('No trending data found. API may have changed or login may be required.');
109
+ }
110
+
111
+ return trends.slice(0, limit);
112
+ },
113
+ });
@@ -0,0 +1,75 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import type { IPage } from '../../types.js';
3
+
4
+ cli({
5
+ site: 'twitter',
6
+ name: 'unblock',
7
+ description: 'Unblock a Twitter user',
8
+ domain: 'x.com',
9
+ strategy: Strategy.UI,
10
+ browser: true,
11
+ args: [
12
+ { name: 'username', type: 'string', positional: true, required: true, help: 'Twitter screen name (without @)' },
13
+ ],
14
+ columns: ['status', 'message'],
15
+ func: async (page: IPage | null, kwargs: any) => {
16
+ if (!page) throw new Error('Requires browser');
17
+ const username = kwargs.username.replace(/^@/, '');
18
+
19
+ await page.goto(`https://x.com/${username}`);
20
+ await page.wait(5);
21
+
22
+ const result = await page.evaluate(`(async () => {
23
+ try {
24
+ let attempts = 0;
25
+ let unblockBtn = null;
26
+
27
+ while (attempts < 20) {
28
+ // Check if not blocked (follow button visible means not blocked)
29
+ const followBtn = document.querySelector('[data-testid$="-follow"]');
30
+ if (followBtn) {
31
+ return { ok: true, message: 'Not blocking @${username} (already unblocked).' };
32
+ }
33
+
34
+ unblockBtn = document.querySelector('[data-testid$="-unblock"]');
35
+ if (unblockBtn) break;
36
+
37
+ await new Promise(r => setTimeout(r, 500));
38
+ attempts++;
39
+ }
40
+
41
+ if (!unblockBtn) {
42
+ return { ok: false, message: 'Could not find Unblock button. Are you logged in?' };
43
+ }
44
+
45
+ // Click the unblock button — this opens a confirmation dialog
46
+ unblockBtn.click();
47
+ await new Promise(r => setTimeout(r, 1000));
48
+
49
+ // Confirm the unblock in the dialog
50
+ const confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
51
+ if (confirmBtn) {
52
+ confirmBtn.click();
53
+ await new Promise(r => setTimeout(r, 1000));
54
+ }
55
+
56
+ // Verify
57
+ const verify = document.querySelector('[data-testid$="-follow"]');
58
+ if (verify) {
59
+ return { ok: true, message: 'Successfully unblocked @${username}.' };
60
+ } else {
61
+ return { ok: false, message: 'Unblock action initiated but UI did not update.' };
62
+ }
63
+ } catch (e) {
64
+ return { ok: false, message: e.toString() };
65
+ }
66
+ })()`);
67
+
68
+ if (result.ok) await page.wait(2);
69
+
70
+ return [{
71
+ status: result.ok ? 'success' : 'failed',
72
+ message: result.message
73
+ }];
74
+ }
75
+ });
@@ -7,6 +7,7 @@ browser: false
7
7
 
8
8
  args:
9
9
  id:
10
+ positional: true
10
11
  type: str
11
12
  required: true
12
13
  description: Topic ID
@@ -17,7 +17,6 @@ cli({
17
17
  func: async (page, kwargs) => {
18
18
  const count = Math.min(kwargs.limit || 30, 50);
19
19
  await page.goto('https://weibo.com');
20
- await page.wait(2);
21
20
  const data = await page.evaluate(`
22
21
  (async () => {
23
22
  const resp = await fetch('/ajax/statuses/hot_band', {credentials: 'include'});
@@ -9,7 +9,7 @@ cli({
9
9
  domain: 'weread.qq.com',
10
10
  strategy: Strategy.COOKIE,
11
11
  args: [
12
- { name: 'bookId', positional: true, required: true, help: 'Book ID (numeric, from search or shelf results)' },
12
+ { name: 'book-id', positional: true, required: true, help: 'Book ID (numeric, from search or shelf results)' },
13
13
  ],
14
14
  columns: ['title', 'author', 'publisher', 'intro', 'category', 'rating'],
15
15
  func: async (page: IPage, args) => {