@jackwener/opencli 1.1.1 → 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 (509) hide show
  1. package/CONTRIBUTING.md +39 -1
  2. package/README.md +9 -10
  3. package/README.zh-CN.md +39 -17
  4. package/SKILL.md +10 -5
  5. package/dist/browser/cdp.d.ts +4 -4
  6. package/dist/browser/cdp.js +39 -16
  7. package/dist/browser/daemon-client.d.ts +2 -1
  8. package/dist/browser/dom-helpers.js +38 -7
  9. package/dist/browser/dom-snapshot.d.ts +86 -0
  10. package/dist/browser/dom-snapshot.js +729 -0
  11. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  12. package/dist/browser/dom-snapshot.test.js +212 -0
  13. package/dist/browser/index.d.ts +2 -0
  14. package/dist/browser/index.js +1 -0
  15. package/dist/browser/page.d.ts +14 -24
  16. package/dist/browser/page.js +37 -4
  17. package/dist/build-manifest.d.ts +11 -4
  18. package/dist/build-manifest.js +59 -21
  19. package/dist/build-manifest.test.js +58 -2
  20. package/dist/cli-manifest.json +3856 -1509
  21. package/dist/cli.js +66 -0
  22. package/dist/clis/barchart/greeks.js +1 -1
  23. package/dist/clis/barchart/options.js +1 -1
  24. package/dist/clis/barchart/quote.js +1 -1
  25. package/dist/clis/bilibili/download.js +1 -1
  26. package/dist/clis/bilibili/following.js +1 -1
  27. package/dist/clis/bilibili/subtitle.js +1 -1
  28. package/dist/clis/bilibili/user-videos.js +1 -1
  29. package/dist/clis/boss/batchgreet.js +10 -97
  30. package/dist/clis/boss/chatlist.js +8 -25
  31. package/dist/clis/boss/chatmsg.js +11 -42
  32. package/dist/clis/boss/common.d.ts +92 -0
  33. package/dist/clis/boss/common.js +223 -0
  34. package/dist/clis/boss/detail.js +7 -49
  35. package/dist/clis/boss/exchange.js +13 -79
  36. package/dist/clis/boss/greet.js +18 -145
  37. package/dist/clis/boss/invite.js +26 -121
  38. package/dist/clis/boss/joblist.js +6 -31
  39. package/dist/clis/boss/mark.js +12 -85
  40. package/dist/clis/boss/recommend.js +10 -49
  41. package/dist/clis/boss/resume.js +18 -118
  42. package/dist/clis/boss/search.js +12 -60
  43. package/dist/clis/boss/send.js +17 -151
  44. package/dist/clis/boss/stats.js +18 -69
  45. package/dist/clis/coupang/add-to-cart.js +1 -1
  46. package/dist/clis/devto/tag.yaml +34 -0
  47. package/dist/clis/devto/top.yaml +29 -0
  48. package/dist/clis/devto/user.yaml +33 -0
  49. package/dist/clis/douban/book-hot.d.ts +1 -0
  50. package/dist/clis/douban/book-hot.js +14 -0
  51. package/dist/clis/douban/marks.d.ts +1 -0
  52. package/dist/clis/douban/marks.js +115 -0
  53. package/dist/clis/douban/movie-hot.d.ts +1 -0
  54. package/dist/clis/douban/movie-hot.js +14 -0
  55. package/dist/clis/douban/reviews.d.ts +1 -0
  56. package/dist/clis/douban/reviews.js +106 -0
  57. package/dist/clis/douban/search.d.ts +1 -0
  58. package/dist/clis/douban/search.js +16 -0
  59. package/dist/clis/douban/shared.d.ts +4 -0
  60. package/dist/clis/douban/shared.js +155 -0
  61. package/dist/clis/douban/subject.yaml +76 -0
  62. package/dist/clis/douban/top250.yaml +70 -0
  63. package/dist/clis/douban/utils.d.ts +35 -0
  64. package/dist/clis/douban/utils.js +48 -0
  65. package/dist/clis/facebook/add-friend.yaml +43 -0
  66. package/dist/clis/facebook/events.yaml +44 -0
  67. package/dist/clis/facebook/feed.yaml +63 -0
  68. package/dist/clis/facebook/friends.yaml +42 -0
  69. package/dist/clis/facebook/groups.yaml +50 -0
  70. package/dist/clis/facebook/join-group.yaml +44 -0
  71. package/dist/clis/facebook/memories.yaml +39 -0
  72. package/dist/clis/facebook/notifications.yaml +40 -0
  73. package/dist/clis/facebook/profile.yaml +37 -0
  74. package/dist/clis/facebook/search.yaml +46 -0
  75. package/dist/clis/google/news.d.ts +5 -0
  76. package/dist/clis/google/news.js +58 -0
  77. package/dist/clis/google/search.d.ts +10 -0
  78. package/dist/clis/google/search.js +127 -0
  79. package/dist/clis/google/suggest.d.ts +5 -0
  80. package/dist/clis/google/suggest.js +34 -0
  81. package/dist/clis/google/trends.d.ts +5 -0
  82. package/dist/clis/google/trends.js +38 -0
  83. package/dist/clis/google/utils.d.ts +9 -0
  84. package/dist/clis/google/utils.js +23 -0
  85. package/dist/clis/google/utils.test.d.ts +1 -0
  86. package/dist/clis/google/utils.test.js +75 -0
  87. package/dist/clis/grok/ask.d.ts +14 -0
  88. package/dist/clis/grok/ask.js +257 -65
  89. package/dist/clis/grok/ask.test.d.ts +1 -0
  90. package/dist/clis/grok/ask.test.js +36 -0
  91. package/dist/clis/instagram/comment.yaml +52 -0
  92. package/dist/clis/instagram/explore.yaml +43 -0
  93. package/dist/clis/instagram/follow.yaml +41 -0
  94. package/dist/clis/instagram/followers.yaml +51 -0
  95. package/dist/clis/instagram/following.yaml +51 -0
  96. package/dist/clis/instagram/like.yaml +46 -0
  97. package/dist/clis/instagram/profile.yaml +42 -0
  98. package/dist/clis/instagram/save.yaml +46 -0
  99. package/dist/clis/instagram/saved.yaml +40 -0
  100. package/dist/clis/instagram/search.yaml +43 -0
  101. package/dist/clis/instagram/unfollow.yaml +38 -0
  102. package/dist/clis/instagram/unlike.yaml +46 -0
  103. package/dist/clis/instagram/unsave.yaml +46 -0
  104. package/dist/clis/instagram/user.yaml +54 -0
  105. package/dist/clis/jike/repost.js +1 -1
  106. package/dist/clis/jimeng/generate.yaml +1 -0
  107. package/dist/clis/linux-do/category.yaml +1 -0
  108. package/dist/clis/lobsters/active.yaml +29 -0
  109. package/dist/clis/lobsters/hot.yaml +29 -0
  110. package/dist/clis/lobsters/newest.yaml +29 -0
  111. package/dist/clis/lobsters/tag.yaml +34 -0
  112. package/dist/clis/medium/feed.d.ts +1 -0
  113. package/dist/clis/medium/feed.js +15 -0
  114. package/dist/clis/medium/search.d.ts +1 -0
  115. package/dist/clis/medium/search.js +15 -0
  116. package/dist/clis/medium/shared.d.ts +5 -0
  117. package/dist/clis/medium/shared.js +78 -0
  118. package/dist/clis/medium/user.d.ts +1 -0
  119. package/dist/clis/medium/user.js +15 -0
  120. package/dist/clis/reddit/comment.js +1 -1
  121. package/dist/clis/reddit/read.js +1 -1
  122. package/dist/clis/reddit/save.js +1 -1
  123. package/dist/clis/reddit/subreddit.yaml +1 -0
  124. package/dist/clis/reddit/subscribe.js +1 -1
  125. package/dist/clis/reddit/upvote.js +1 -1
  126. package/dist/clis/sinablog/article.d.ts +1 -0
  127. package/dist/clis/sinablog/article.js +14 -0
  128. package/dist/clis/sinablog/hot.d.ts +1 -0
  129. package/dist/clis/sinablog/hot.js +14 -0
  130. package/dist/clis/sinablog/search.d.ts +1 -0
  131. package/dist/clis/sinablog/search.js +51 -0
  132. package/dist/clis/sinablog/shared.d.ts +7 -0
  133. package/dist/clis/sinablog/shared.js +187 -0
  134. package/dist/clis/sinablog/user.d.ts +1 -0
  135. package/dist/clis/sinablog/user.js +15 -0
  136. package/dist/clis/substack/feed.d.ts +1 -0
  137. package/dist/clis/substack/feed.js +15 -0
  138. package/dist/clis/substack/publication.d.ts +1 -0
  139. package/dist/clis/substack/publication.js +15 -0
  140. package/dist/clis/substack/search.d.ts +1 -0
  141. package/dist/clis/substack/search.js +77 -0
  142. package/dist/clis/substack/shared.d.ts +4 -0
  143. package/dist/clis/substack/shared.js +129 -0
  144. package/dist/clis/tiktok/comment.yaml +66 -0
  145. package/dist/clis/tiktok/explore.yaml +39 -0
  146. package/dist/clis/tiktok/follow.yaml +39 -0
  147. package/dist/clis/tiktok/following.yaml +46 -0
  148. package/dist/clis/tiktok/friends.yaml +47 -0
  149. package/dist/clis/tiktok/like.yaml +38 -0
  150. package/dist/clis/tiktok/live.yaml +51 -0
  151. package/dist/clis/tiktok/notifications.yaml +52 -0
  152. package/dist/clis/tiktok/profile.yaml +45 -0
  153. package/dist/clis/tiktok/save.yaml +34 -0
  154. package/dist/clis/tiktok/search.yaml +46 -0
  155. package/dist/clis/tiktok/unfollow.yaml +44 -0
  156. package/dist/clis/tiktok/unlike.yaml +38 -0
  157. package/dist/clis/tiktok/unsave.yaml +36 -0
  158. package/dist/clis/tiktok/user.yaml +44 -0
  159. package/dist/clis/twitter/download.d.ts +1 -1
  160. package/dist/clis/twitter/download.js +3 -3
  161. package/dist/clis/twitter/followers.js +1 -1
  162. package/dist/clis/twitter/following.js +1 -1
  163. package/dist/clis/twitter/thread.js +1 -1
  164. package/dist/clis/twitter/timeline.d.ts +23 -0
  165. package/dist/clis/twitter/timeline.js +42 -14
  166. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  167. package/dist/clis/twitter/timeline.test.js +102 -0
  168. package/dist/clis/wikipedia/random.d.ts +1 -0
  169. package/dist/clis/wikipedia/random.js +19 -0
  170. package/dist/clis/wikipedia/search.js +3 -3
  171. package/dist/clis/wikipedia/summary.js +4 -9
  172. package/dist/clis/wikipedia/trending.d.ts +1 -0
  173. package/dist/clis/wikipedia/trending.js +35 -0
  174. package/dist/clis/wikipedia/utils.d.ts +28 -0
  175. package/dist/clis/wikipedia/utils.js +13 -0
  176. package/dist/clis/xiaohongshu/creator-note-detail.js +1 -1
  177. package/dist/clis/xiaohongshu/creator-note-detail.test.js +2 -0
  178. package/dist/clis/xiaohongshu/creator-notes.test.js +2 -0
  179. package/dist/clis/xiaohongshu/download.js +1 -1
  180. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  181. package/dist/clis/xueqiu/search.yaml +2 -1
  182. package/dist/clis/xueqiu/stock.yaml +2 -0
  183. package/dist/clis/yahoo-finance/quote.js +1 -1
  184. package/dist/commanderAdapter.js +13 -7
  185. package/dist/discovery.d.ts +8 -0
  186. package/dist/discovery.js +105 -19
  187. package/dist/doctor.js +3 -1
  188. package/dist/doctor.test.js +46 -2
  189. package/dist/engine.test.d.ts +0 -3
  190. package/dist/engine.test.js +74 -6
  191. package/dist/execution.d.ts +4 -2
  192. package/dist/execution.js +31 -7
  193. package/dist/explore.d.ts +76 -3
  194. package/dist/explore.js +11 -4
  195. package/dist/generate.d.ts +41 -2
  196. package/dist/generate.js +5 -4
  197. package/dist/main.js +2 -1
  198. package/dist/pipeline/executor.d.ts +2 -2
  199. package/dist/pipeline/executor.js +2 -2
  200. package/dist/pipeline/executor.test.js +33 -6
  201. package/dist/pipeline/registry.d.ts +1 -1
  202. package/dist/pipeline/steps/browser.d.ts +7 -7
  203. package/dist/pipeline/steps/browser.js +15 -7
  204. package/dist/pipeline/steps/fetch.d.ts +1 -1
  205. package/dist/pipeline/steps/fetch.js +11 -7
  206. package/dist/pipeline/steps/transform.d.ts +6 -5
  207. package/dist/pipeline/steps/transform.js +30 -9
  208. package/dist/pipeline/template.d.ts +6 -6
  209. package/dist/pipeline/template.js +43 -5
  210. package/dist/pipeline/template.test.js +18 -0
  211. package/dist/pipeline/transform.test.js +11 -0
  212. package/dist/plugin.d.ts +31 -0
  213. package/dist/plugin.js +216 -0
  214. package/dist/plugin.test.d.ts +4 -0
  215. package/dist/plugin.test.js +76 -0
  216. package/dist/registry-api.d.ts +11 -0
  217. package/dist/registry-api.js +9 -0
  218. package/dist/registry.d.ts +11 -0
  219. package/dist/registry.js +6 -1
  220. package/dist/synthesize.d.ts +94 -4
  221. package/dist/synthesize.js +5 -4
  222. package/dist/types.d.ts +39 -26
  223. package/dist/validate.js +8 -2
  224. package/docs/.vitepress/config.mts +6 -4
  225. package/docs/adapters/browser/barchart.md +6 -5
  226. package/docs/adapters/browser/bilibili.md +9 -0
  227. package/docs/adapters/browser/devto.md +35 -0
  228. package/docs/adapters/browser/douban.md +38 -0
  229. package/docs/adapters/browser/facebook.md +36 -0
  230. package/docs/adapters/browser/google.md +62 -0
  231. package/docs/adapters/browser/grok.md +26 -8
  232. package/docs/adapters/browser/instagram.md +46 -0
  233. package/docs/adapters/browser/lobsters.md +32 -0
  234. package/docs/adapters/browser/medium.md +32 -0
  235. package/docs/adapters/browser/reddit.md +9 -0
  236. package/docs/adapters/browser/sinablog.md +36 -0
  237. package/docs/adapters/browser/substack.md +38 -0
  238. package/docs/adapters/browser/tiktok.md +68 -0
  239. package/docs/adapters/browser/wikipedia.md +11 -2
  240. package/docs/adapters/browser/xueqiu.md +10 -0
  241. package/docs/adapters/browser/yahoo-finance.md +6 -5
  242. package/docs/adapters/desktop/antigravity.md +6 -0
  243. package/docs/adapters/desktop/chatgpt.md +2 -1
  244. package/docs/adapters/desktop/codex.md +5 -1
  245. package/docs/adapters/desktop/cursor.md +4 -0
  246. package/docs/adapters/desktop/discord.md +7 -7
  247. package/docs/adapters/index.md +1 -4
  248. package/docs/guide/getting-started.md +1 -0
  249. package/docs/guide/plugins.md +153 -0
  250. package/docs/zh/guide/plugins.md +107 -0
  251. package/extension/src/background.ts +18 -11
  252. package/package.json +10 -5
  253. package/scripts/clean-dist.cjs +13 -0
  254. package/src/browser/cdp.ts +71 -31
  255. package/src/browser/daemon-client.ts +2 -1
  256. package/src/browser/dom-helpers.ts +38 -7
  257. package/src/browser/dom-snapshot.test.ts +249 -0
  258. package/src/browser/dom-snapshot.ts +770 -0
  259. package/src/browser/index.ts +2 -0
  260. package/src/browser/page.ts +50 -19
  261. package/src/build-manifest.test.ts +70 -2
  262. package/src/build-manifest.ts +94 -26
  263. package/src/cli.ts +71 -2
  264. package/src/clis/barchart/greeks.ts +1 -1
  265. package/src/clis/barchart/options.ts +1 -1
  266. package/src/clis/barchart/quote.ts +1 -1
  267. package/src/clis/bilibili/download.ts +1 -1
  268. package/src/clis/bilibili/following.ts +1 -1
  269. package/src/clis/bilibili/subtitle.ts +1 -1
  270. package/src/clis/bilibili/user-videos.ts +1 -1
  271. package/src/clis/boss/batchgreet.ts +14 -106
  272. package/src/clis/boss/chatlist.ts +12 -26
  273. package/src/clis/boss/chatmsg.ts +16 -40
  274. package/src/clis/boss/common.ts +287 -0
  275. package/src/clis/boss/detail.ts +8 -54
  276. package/src/clis/boss/exchange.ts +15 -89
  277. package/src/clis/boss/greet.ts +23 -160
  278. package/src/clis/boss/invite.ts +36 -133
  279. package/src/clis/boss/joblist.ts +7 -36
  280. package/src/clis/boss/mark.ts +13 -94
  281. package/src/clis/boss/recommend.ts +12 -57
  282. package/src/clis/boss/resume.ts +19 -124
  283. package/src/clis/boss/search.ts +13 -66
  284. package/src/clis/boss/send.ts +21 -161
  285. package/src/clis/boss/stats.ts +19 -74
  286. package/src/clis/coupang/add-to-cart.ts +1 -1
  287. package/src/clis/devto/tag.yaml +34 -0
  288. package/src/clis/devto/top.yaml +29 -0
  289. package/src/clis/devto/user.yaml +33 -0
  290. package/src/clis/douban/book-hot.ts +15 -0
  291. package/src/clis/douban/marks.ts +135 -0
  292. package/src/clis/douban/movie-hot.ts +15 -0
  293. package/src/clis/douban/reviews.ts +127 -0
  294. package/src/clis/douban/search.ts +17 -0
  295. package/src/clis/douban/shared.ts +165 -0
  296. package/src/clis/douban/subject.yaml +76 -0
  297. package/src/clis/douban/top250.yaml +70 -0
  298. package/src/clis/douban/utils.ts +81 -0
  299. package/src/clis/facebook/add-friend.yaml +43 -0
  300. package/src/clis/facebook/events.yaml +44 -0
  301. package/src/clis/facebook/feed.yaml +63 -0
  302. package/src/clis/facebook/friends.yaml +42 -0
  303. package/src/clis/facebook/groups.yaml +50 -0
  304. package/src/clis/facebook/join-group.yaml +44 -0
  305. package/src/clis/facebook/memories.yaml +39 -0
  306. package/src/clis/facebook/notifications.yaml +40 -0
  307. package/src/clis/facebook/profile.yaml +37 -0
  308. package/src/clis/facebook/search.yaml +46 -0
  309. package/src/clis/google/news.ts +66 -0
  310. package/src/clis/google/search.ts +133 -0
  311. package/src/clis/google/suggest.ts +40 -0
  312. package/src/clis/google/trends.ts +44 -0
  313. package/src/clis/google/utils.test.ts +82 -0
  314. package/src/clis/google/utils.ts +24 -0
  315. package/src/clis/grok/ask.test.ts +53 -0
  316. package/src/clis/grok/ask.ts +300 -69
  317. package/src/clis/instagram/comment.yaml +52 -0
  318. package/src/clis/instagram/explore.yaml +43 -0
  319. package/src/clis/instagram/follow.yaml +41 -0
  320. package/src/clis/instagram/followers.yaml +51 -0
  321. package/src/clis/instagram/following.yaml +51 -0
  322. package/src/clis/instagram/like.yaml +46 -0
  323. package/src/clis/instagram/profile.yaml +42 -0
  324. package/src/clis/instagram/save.yaml +46 -0
  325. package/src/clis/instagram/saved.yaml +40 -0
  326. package/src/clis/instagram/search.yaml +43 -0
  327. package/src/clis/instagram/unfollow.yaml +38 -0
  328. package/src/clis/instagram/unlike.yaml +46 -0
  329. package/src/clis/instagram/unsave.yaml +46 -0
  330. package/src/clis/instagram/user.yaml +54 -0
  331. package/src/clis/jike/repost.ts +1 -1
  332. package/src/clis/jimeng/generate.yaml +1 -0
  333. package/src/clis/linux-do/category.yaml +1 -0
  334. package/src/clis/lobsters/active.yaml +29 -0
  335. package/src/clis/lobsters/hot.yaml +29 -0
  336. package/src/clis/lobsters/newest.yaml +29 -0
  337. package/src/clis/lobsters/tag.yaml +34 -0
  338. package/src/clis/medium/feed.ts +16 -0
  339. package/src/clis/medium/search.ts +16 -0
  340. package/src/clis/medium/shared.ts +83 -0
  341. package/src/clis/medium/user.ts +16 -0
  342. package/src/clis/reddit/comment.ts +1 -1
  343. package/src/clis/reddit/read.ts +1 -1
  344. package/src/clis/reddit/save.ts +1 -1
  345. package/src/clis/reddit/subreddit.yaml +1 -0
  346. package/src/clis/reddit/subscribe.ts +1 -1
  347. package/src/clis/reddit/upvote.ts +1 -1
  348. package/src/clis/sinablog/article.ts +15 -0
  349. package/src/clis/sinablog/hot.ts +15 -0
  350. package/src/clis/sinablog/search.ts +56 -0
  351. package/src/clis/sinablog/shared.ts +198 -0
  352. package/src/clis/sinablog/user.ts +16 -0
  353. package/src/clis/substack/feed.ts +16 -0
  354. package/src/clis/substack/publication.ts +16 -0
  355. package/src/clis/substack/search.ts +91 -0
  356. package/src/clis/substack/shared.ts +132 -0
  357. package/src/clis/tiktok/comment.yaml +66 -0
  358. package/src/clis/tiktok/explore.yaml +39 -0
  359. package/src/clis/tiktok/follow.yaml +39 -0
  360. package/src/clis/tiktok/following.yaml +46 -0
  361. package/src/clis/tiktok/friends.yaml +47 -0
  362. package/src/clis/tiktok/like.yaml +38 -0
  363. package/src/clis/tiktok/live.yaml +51 -0
  364. package/src/clis/tiktok/notifications.yaml +52 -0
  365. package/src/clis/tiktok/profile.yaml +45 -0
  366. package/src/clis/tiktok/save.yaml +34 -0
  367. package/src/clis/tiktok/search.yaml +46 -0
  368. package/src/clis/tiktok/unfollow.yaml +44 -0
  369. package/src/clis/tiktok/unlike.yaml +38 -0
  370. package/src/clis/tiktok/unsave.yaml +36 -0
  371. package/src/clis/tiktok/user.yaml +44 -0
  372. package/src/clis/twitter/download.ts +3 -3
  373. package/src/clis/twitter/followers.ts +1 -1
  374. package/src/clis/twitter/following.ts +1 -1
  375. package/src/clis/twitter/thread.ts +1 -1
  376. package/src/clis/twitter/timeline.test.ts +109 -0
  377. package/src/clis/twitter/timeline.ts +59 -19
  378. package/src/clis/wikipedia/random.ts +19 -0
  379. package/src/clis/wikipedia/search.ts +10 -4
  380. package/src/clis/wikipedia/summary.ts +4 -9
  381. package/src/clis/wikipedia/trending.ts +41 -0
  382. package/src/clis/wikipedia/utils.ts +31 -0
  383. package/src/clis/xiaohongshu/creator-note-detail.test.ts +2 -0
  384. package/src/clis/xiaohongshu/creator-note-detail.ts +1 -1
  385. package/src/clis/xiaohongshu/creator-notes.test.ts +2 -0
  386. package/src/clis/xiaohongshu/download.ts +1 -1
  387. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  388. package/src/clis/xueqiu/search.yaml +2 -1
  389. package/src/clis/xueqiu/stock.yaml +2 -0
  390. package/src/clis/yahoo-finance/quote.ts +1 -1
  391. package/src/commanderAdapter.ts +17 -10
  392. package/src/discovery.ts +134 -24
  393. package/src/doctor.test.ts +59 -2
  394. package/src/doctor.ts +4 -2
  395. package/src/engine.test.ts +79 -6
  396. package/src/execution.ts +42 -16
  397. package/src/explore.ts +77 -9
  398. package/src/generate.ts +58 -9
  399. package/src/main.ts +2 -1
  400. package/src/pipeline/executor.test.ts +35 -6
  401. package/src/pipeline/executor.ts +11 -7
  402. package/src/pipeline/registry.ts +3 -3
  403. package/src/pipeline/steps/browser.ts +24 -15
  404. package/src/pipeline/steps/fetch.ts +18 -13
  405. package/src/pipeline/steps/transform.ts +40 -15
  406. package/src/pipeline/template.test.ts +18 -0
  407. package/src/pipeline/template.ts +86 -13
  408. package/src/pipeline/transform.test.ts +15 -2
  409. package/src/plugin.test.ts +86 -0
  410. package/src/plugin.ts +254 -0
  411. package/src/registry-api.ts +12 -0
  412. package/src/registry.ts +19 -1
  413. package/src/synthesize.ts +102 -21
  414. package/src/types.ts +44 -12
  415. package/src/validate.ts +19 -4
  416. package/tests/e2e/browser-public.test.ts +11 -0
  417. package/tests/e2e/public-commands.test.ts +64 -0
  418. package/dist/clis/feishu/new.d.ts +0 -1
  419. package/dist/clis/feishu/new.js +0 -27
  420. package/dist/clis/feishu/read.d.ts +0 -1
  421. package/dist/clis/feishu/read.js +0 -40
  422. package/dist/clis/feishu/search.d.ts +0 -1
  423. package/dist/clis/feishu/search.js +0 -30
  424. package/dist/clis/feishu/send.d.ts +0 -1
  425. package/dist/clis/feishu/send.js +0 -39
  426. package/dist/clis/feishu/status.d.ts +0 -1
  427. package/dist/clis/feishu/status.js +0 -28
  428. package/dist/clis/neteasemusic/like.d.ts +0 -1
  429. package/dist/clis/neteasemusic/like.js +0 -25
  430. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  431. package/dist/clis/neteasemusic/lyrics.js +0 -47
  432. package/dist/clis/neteasemusic/next.d.ts +0 -1
  433. package/dist/clis/neteasemusic/next.js +0 -26
  434. package/dist/clis/neteasemusic/play.d.ts +0 -1
  435. package/dist/clis/neteasemusic/play.js +0 -26
  436. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  437. package/dist/clis/neteasemusic/playing.js +0 -59
  438. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  439. package/dist/clis/neteasemusic/playlist.js +0 -46
  440. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  441. package/dist/clis/neteasemusic/prev.js +0 -25
  442. package/dist/clis/neteasemusic/search.d.ts +0 -1
  443. package/dist/clis/neteasemusic/search.js +0 -52
  444. package/dist/clis/neteasemusic/status.d.ts +0 -1
  445. package/dist/clis/neteasemusic/status.js +0 -16
  446. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  447. package/dist/clis/neteasemusic/volume.js +0 -54
  448. package/dist/clis/wechat/chats.d.ts +0 -1
  449. package/dist/clis/wechat/chats.js +0 -28
  450. package/dist/clis/wechat/contacts.d.ts +0 -1
  451. package/dist/clis/wechat/contacts.js +0 -28
  452. package/dist/clis/wechat/read.d.ts +0 -1
  453. package/dist/clis/wechat/read.js +0 -58
  454. package/dist/clis/wechat/search.d.ts +0 -1
  455. package/dist/clis/wechat/search.js +0 -31
  456. package/dist/clis/wechat/send.d.ts +0 -1
  457. package/dist/clis/wechat/send.js +0 -42
  458. package/dist/clis/wechat/status.d.ts +0 -1
  459. package/dist/clis/wechat/status.js +0 -29
  460. package/dist/pipeline.d.ts +0 -7
  461. package/dist/pipeline.js +0 -7
  462. package/docs/adapters/browser/github.md +0 -26
  463. package/docs/adapters/desktop/feishu.md +0 -20
  464. package/docs/adapters/desktop/neteasemusic.md +0 -31
  465. package/docs/adapters/desktop/wechat.md +0 -28
  466. package/src/clis/antigravity/README.md +0 -5
  467. package/src/clis/antigravity/README.zh-CN.md +0 -51
  468. package/src/clis/chaoxing/README.md +0 -14
  469. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  470. package/src/clis/chatgpt/README.md +0 -5
  471. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  472. package/src/clis/chatwise/README.md +0 -5
  473. package/src/clis/chatwise/README.zh-CN.md +0 -38
  474. package/src/clis/codex/README.md +0 -5
  475. package/src/clis/codex/README.zh-CN.md +0 -33
  476. package/src/clis/cursor/README.md +0 -5
  477. package/src/clis/cursor/README.zh-CN.md +0 -33
  478. package/src/clis/discord-app/README.md +0 -5
  479. package/src/clis/discord-app/README.zh-CN.md +0 -28
  480. package/src/clis/feishu/README.md +0 -5
  481. package/src/clis/feishu/README.zh-CN.md +0 -20
  482. package/src/clis/feishu/new.ts +0 -32
  483. package/src/clis/feishu/read.ts +0 -48
  484. package/src/clis/feishu/search.ts +0 -35
  485. package/src/clis/feishu/send.ts +0 -46
  486. package/src/clis/feishu/status.ts +0 -34
  487. package/src/clis/neteasemusic/README.md +0 -5
  488. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  489. package/src/clis/neteasemusic/like.ts +0 -28
  490. package/src/clis/neteasemusic/lyrics.ts +0 -53
  491. package/src/clis/neteasemusic/next.ts +0 -30
  492. package/src/clis/neteasemusic/play.ts +0 -30
  493. package/src/clis/neteasemusic/playing.ts +0 -62
  494. package/src/clis/neteasemusic/playlist.ts +0 -51
  495. package/src/clis/neteasemusic/prev.ts +0 -29
  496. package/src/clis/neteasemusic/search.ts +0 -58
  497. package/src/clis/neteasemusic/status.ts +0 -18
  498. package/src/clis/neteasemusic/volume.ts +0 -61
  499. package/src/clis/notion/README.md +0 -5
  500. package/src/clis/notion/README.zh-CN.md +0 -29
  501. package/src/clis/wechat/README.md +0 -5
  502. package/src/clis/wechat/README.zh-CN.md +0 -28
  503. package/src/clis/wechat/chats.ts +0 -33
  504. package/src/clis/wechat/contacts.ts +0 -33
  505. package/src/clis/wechat/read.ts +0 -72
  506. package/src/clis/wechat/search.ts +0 -36
  507. package/src/clis/wechat/send.ts +0 -49
  508. package/src/clis/wechat/status.ts +0 -35
  509. package/src/pipeline.ts +0 -8
@@ -0,0 +1,70 @@
1
+ site: douban
2
+ name: top250
3
+ description: 豆瓣电影 Top250
4
+ domain: movie.douban.com
5
+ strategy: cookie
6
+ browser: true
7
+
8
+ args:
9
+ limit:
10
+ type: int
11
+ default: 250
12
+ description: 返回结果数量
13
+
14
+ pipeline:
15
+ - navigate: https://movie.douban.com/top250
16
+
17
+ - evaluate: |
18
+ async () => {
19
+ const results = [];
20
+ const limit = ${{ args.limit }};
21
+
22
+ const parsePage = (doc) => {
23
+ const items = doc.querySelectorAll('.item');
24
+ for (const item of items) {
25
+ if (results.length >= limit) break;
26
+
27
+ const rankEl = item.querySelector('.pic em');
28
+ const linkEl = item.querySelector('a');
29
+ const titleEl = item.querySelector('.title');
30
+ const ratingEl = item.querySelector('.rating_num');
31
+
32
+ const href = linkEl?.href || '';
33
+ const matchResult = href.match(/subject\/(\d+)/);
34
+ const id = matchResult ? matchResult[1] : '';
35
+
36
+ const title = titleEl?.textContent?.trim() || '';
37
+ const rank = parseInt(rankEl?.textContent || '0', 10);
38
+ const rating = ratingEl?.textContent?.trim() || '';
39
+
40
+ if (id && title) {
41
+ results.push({
42
+ rank: rank || results.length + 1,
43
+ id,
44
+ title,
45
+ rating: rating ? parseFloat(rating) : 0,
46
+ url: href
47
+ });
48
+ }
49
+ }
50
+ };
51
+
52
+ parsePage(document);
53
+
54
+ for (let start = 25; start < 250 && results.length < limit; start += 25) {
55
+ const resp = await fetch(`https://movie.douban.com/top250?start=${start}`);
56
+ if (!resp.ok) break;
57
+ const html = await resp.text();
58
+ if (!html) break;
59
+
60
+ const doc = new DOMParser().parseFromString(html, 'text/html');
61
+ parsePage(doc);
62
+ await new Promise(r => setTimeout(r, 150));
63
+ }
64
+
65
+ return results;
66
+ }
67
+
68
+ - limit: ${{ args.limit }}
69
+
70
+ columns: [rank, id, title, rating, url]
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Douban movie adapter utilities.
3
+ */
4
+ import type { IPage } from '../../types.js';
5
+ /**
6
+ * Get current user's Douban ID from movie.douban.com/mine page
7
+ */
8
+ export declare function getSelfUid(page: IPage): Promise<string>;
9
+ /**
10
+ * Douban mark (viewing record) interface
11
+ */
12
+ export interface DoubanMark {
13
+ movieId: string;
14
+ title: string;
15
+ year: string;
16
+ myRating: number | null;
17
+ myStatus: 'collect' | 'wish' | 'do';
18
+ myComment: string;
19
+ myDate: string;
20
+ url: string;
21
+ }
22
+ /**
23
+ * Douban review interface
24
+ */
25
+ export interface DoubanReview {
26
+ reviewId: string;
27
+ movieId: string;
28
+ movieTitle: string;
29
+ title: string;
30
+ content: string;
31
+ myRating: number;
32
+ createdAt: string;
33
+ votes: number;
34
+ url: string;
35
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Douban movie adapter utilities.
3
+ */
4
+ /**
5
+ * Get current user's Douban ID from movie.douban.com/mine page
6
+ */
7
+ export async function getSelfUid(page) {
8
+ await page.goto('https://movie.douban.com/mine');
9
+ await page.wait({ time: 2 });
10
+ const uid = await page.evaluate(`
11
+ (() => {
12
+ // 方案1: 尝试从全局变量获取
13
+ if (window.__DATA__ && window.__DATA__.uid) {
14
+ return window.__DATA__.uid;
15
+ }
16
+
17
+ // 方案2: 从导航栏用户链接获取
18
+ const navUserLink = document.querySelector('.nav-user-account a');
19
+ if (navUserLink) {
20
+ const href = navUserLink.href || '';
21
+ const match = href.match(/people\\/([^/]+)/);
22
+ if (match) return match[1];
23
+ }
24
+
25
+ // 方案3: 从页面中的个人主页链接获取
26
+ const profileLink = document.querySelector('a[href*="/people/"]');
27
+ if (profileLink) {
28
+ const href = profileLink.getAttribute('href') || profileLink.href || '';
29
+ const match = href.match(/people\\/([^/]+)/);
30
+ if (match) return match[1];
31
+ }
32
+
33
+ // 方案4: 从头部用户名区域获取
34
+ const userLink = document.querySelector('.global-nav-items a[href*="/people/"]');
35
+ if (userLink) {
36
+ const href = userLink.getAttribute('href') || userLink.href || '';
37
+ const match = href.match(/people\\/([^/]+)/);
38
+ if (match) return match[1];
39
+ }
40
+
41
+ return '';
42
+ })()
43
+ `);
44
+ if (!uid) {
45
+ throw new Error('Not logged in to Douban. Please login in Chrome first.');
46
+ }
47
+ return uid;
48
+ }
@@ -0,0 +1,43 @@
1
+ site: facebook
2
+ name: add-friend
3
+ description: Send a friend request on Facebook
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ username:
8
+ type: str
9
+ required: true
10
+ positional: true
11
+ description: Facebook username or profile URL
12
+
13
+ pipeline:
14
+ - navigate:
15
+ url: https://www.facebook.com/${{ args.username }}
16
+ settleMs: 3000
17
+
18
+ - evaluate: |
19
+ (async () => {
20
+ const username = ${{ args.username | json }};
21
+ // Find "Add Friend" button
22
+ const buttons = Array.from(document.querySelectorAll('[role="button"]'));
23
+ const addBtn = buttons.find(b => {
24
+ const text = b.textContent.trim();
25
+ return text === '加好友' || text === 'Add Friend' || text === 'Add friend';
26
+ });
27
+
28
+ if (!addBtn) {
29
+ // Check if already friends
30
+ const isFriend = buttons.some(b => {
31
+ const t = b.textContent.trim();
32
+ return t === '好友' || t === 'Friends' || t.includes('已发送') || t.includes('Pending');
33
+ });
34
+ if (isFriend) return [{ status: 'Already friends or request pending', username }];
35
+ return [{ status: 'Add Friend button not found', username }];
36
+ }
37
+
38
+ addBtn.click();
39
+ await new Promise(r => setTimeout(r, 1500));
40
+ return [{ status: 'Friend request sent', username }];
41
+ })()
42
+
43
+ columns: [status, username]
@@ -0,0 +1,44 @@
1
+ site: facebook
2
+ name: events
3
+ description: Browse Facebook event categories
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ limit:
8
+ type: int
9
+ default: 15
10
+ description: Number of categories
11
+
12
+ pipeline:
13
+ - navigate:
14
+ url: https://www.facebook.com/events
15
+ settleMs: 3000
16
+
17
+ - evaluate: |
18
+ (() => {
19
+ const limit = ${{ args.limit }};
20
+ // Try actual event items first
21
+ const articles = document.querySelectorAll('[role="article"]');
22
+ if (articles.length > 0) {
23
+ return Array.from(articles).slice(0, limit).map((el, i) => ({
24
+ index: i + 1,
25
+ name: el.textContent.trim().replace(/\s+/g, ' ').substring(0, 120),
26
+ }));
27
+ }
28
+
29
+ // List event categories from sidebar navigation
30
+ const links = Array.from(document.querySelectorAll('[role="navigation"] a'))
31
+ .filter(a => {
32
+ const href = a.href || '';
33
+ const text = a.textContent.trim();
34
+ return href.includes('/events/') && text.length > 1 && text.length < 60 &&
35
+ !href.includes('create');
36
+ });
37
+
38
+ return links.slice(0, limit).map((a, i) => ({
39
+ index: i + 1,
40
+ name: a.textContent.trim(),
41
+ }));
42
+ })()
43
+
44
+ columns: [index, name]
@@ -0,0 +1,63 @@
1
+ site: facebook
2
+ name: feed
3
+ description: Get your Facebook news feed
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ limit:
8
+ type: int
9
+ default: 10
10
+ description: Number of posts
11
+
12
+ pipeline:
13
+ - navigate:
14
+ url: https://www.facebook.com/
15
+ settleMs: 4000
16
+
17
+ - evaluate: |
18
+ (() => {
19
+ const limit = ${{ args.limit }};
20
+ const posts = document.querySelectorAll('[role="article"]');
21
+ return Array.from(posts)
22
+ .filter(el => {
23
+ const text = el.textContent.trim();
24
+ // Filter out "People you may know" suggestions (both CN and EN)
25
+ return text.length > 30 &&
26
+ !text.startsWith('可能认识') &&
27
+ !text.startsWith('People you may know') &&
28
+ !text.startsWith('People You May Know');
29
+ })
30
+ .slice(0, limit)
31
+ .map((el, i) => {
32
+ // Author from header link
33
+ const headerLink = el.querySelector('h2 a, h3 a, h4 a, strong a');
34
+ const author = headerLink ? headerLink.textContent.trim() : '';
35
+
36
+ // Post text: grab visible spans, filter noise
37
+ const spans = Array.from(el.querySelectorAll('div[dir="auto"]'))
38
+ .map(s => s.textContent.trim())
39
+ .filter(t => t.length > 10 && t.length < 500);
40
+ const content = spans.length > 0 ? spans[0] : '';
41
+
42
+ // Engagement: find like/comment/share counts (CN + EN)
43
+ const allText = el.textContent;
44
+ const likesMatch = allText.match(/所有心情:([\d,.\s]*[\d万亿KMk]+)/) ||
45
+ allText.match(/All:\s*([\d,.KMk]+)/) ||
46
+ allText.match(/([\d,.KMk]+)\s*(?:likes?|reactions?)/i);
47
+ const commentsMatch = allText.match(/([\d,.]+\s*[万亿]?)\s*条评论/) ||
48
+ allText.match(/([\d,.KMk]+)\s*comments?/i);
49
+ const sharesMatch = allText.match(/([\d,.]+\s*[万亿]?)\s*次分享/) ||
50
+ allText.match(/([\d,.KMk]+)\s*shares?/i);
51
+
52
+ return {
53
+ index: i + 1,
54
+ author: author.substring(0, 50),
55
+ content: content.replace(/\n/g, ' ').substring(0, 120),
56
+ likes: likesMatch ? likesMatch[1] : '-',
57
+ comments: commentsMatch ? commentsMatch[1] : '-',
58
+ shares: sharesMatch ? sharesMatch[1] : '-',
59
+ };
60
+ });
61
+ })()
62
+
63
+ columns: [index, author, content, likes, comments, shares]
@@ -0,0 +1,42 @@
1
+ site: facebook
2
+ name: friends
3
+ description: Get Facebook friend suggestions
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ limit:
8
+ type: int
9
+ default: 10
10
+ description: Number of friend suggestions
11
+
12
+ pipeline:
13
+ - navigate:
14
+ url: https://www.facebook.com/friends
15
+ settleMs: 3000
16
+
17
+ - evaluate: |
18
+ (() => {
19
+ const limit = ${{ args.limit }};
20
+ const items = document.querySelectorAll('[role="listitem"]');
21
+ return Array.from(items)
22
+ .slice(0, limit)
23
+ .map((el, i) => {
24
+ const text = el.textContent.trim().replace(/\s+/g, ' ');
25
+ // Extract mutual info if present (before name extraction to avoid pollution)
26
+ const mutualMatch = text.match(/([\d,]+)\s*位.*(?:关注|共同|mutual)/);
27
+ // Extract name: remove mutual info, action buttons, etc.
28
+ let name = text
29
+ .replace(/[\d,]+\s*位.*(?:关注了|共同好友|mutual friends?)/, '')
30
+ .replace(/加好友.*/, '').replace(/Add [Ff]riend.*/, '')
31
+ .replace(/移除$/, '').replace(/Remove$/, '')
32
+ .trim();
33
+ return {
34
+ index: i + 1,
35
+ name: name.substring(0, 50),
36
+ mutual: mutualMatch ? mutualMatch[1] : '-',
37
+ };
38
+ })
39
+ .filter(item => item.name.length > 0);
40
+ })()
41
+
42
+ columns: [index, name, mutual]
@@ -0,0 +1,50 @@
1
+ site: facebook
2
+ name: groups
3
+ description: List your Facebook groups
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ limit:
8
+ type: int
9
+ default: 20
10
+ description: Number of groups
11
+
12
+ pipeline:
13
+ - navigate:
14
+ url: https://www.facebook.com/groups/feed/
15
+ settleMs: 3000
16
+
17
+ - evaluate: |
18
+ (() => {
19
+ const limit = ${{ args.limit }};
20
+ const links = Array.from(document.querySelectorAll('a'))
21
+ .filter(a => {
22
+ const href = a.href || '';
23
+ return href.includes('/groups/') &&
24
+ !href.includes('/feed') &&
25
+ !href.includes('/discover') &&
26
+ !href.includes('/joins') &&
27
+ !href.includes('category=create') &&
28
+ a.textContent.trim().length > 2;
29
+ });
30
+
31
+ // Deduplicate by href
32
+ const seen = new Set();
33
+ const groups = [];
34
+ for (const a of links) {
35
+ const href = a.href.split('?')[0];
36
+ if (seen.has(href)) continue;
37
+ seen.add(href);
38
+ const raw = a.textContent.trim().replace(/\s+/g, ' ');
39
+ // Split name from "上次发帖" info
40
+ const parts = raw.split(/上次发帖|Last post/);
41
+ groups.push({
42
+ name: (parts[0] || '').trim().substring(0, 60),
43
+ last_post: parts[1] ? parts[1].replace(/^[::]/, '').trim() : '-',
44
+ url: href,
45
+ });
46
+ }
47
+ return groups.slice(0, limit).map((g, i) => ({ index: i + 1, ...g }));
48
+ })()
49
+
50
+ columns: [index, name, last_post, url]
@@ -0,0 +1,44 @@
1
+ site: facebook
2
+ name: join-group
3
+ description: Join a Facebook group
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ group:
8
+ type: str
9
+ required: true
10
+ positional: true
11
+ description: Group ID or URL path (e.g. '1876150192925481' or group name)
12
+
13
+ pipeline:
14
+ - navigate:
15
+ url: https://www.facebook.com/groups/${{ args.group }}
16
+ settleMs: 3000
17
+
18
+ - evaluate: |
19
+ (async () => {
20
+ const group = ${{ args.group | json }};
21
+ const groupName = document.querySelector('h1')?.textContent?.trim() || group;
22
+
23
+ // Find "Join Group" button
24
+ const buttons = Array.from(document.querySelectorAll('[role="button"]'));
25
+ const joinBtn = buttons.find(b => {
26
+ const text = b.textContent.trim();
27
+ return text === '加入小组' || text === 'Join group' || text === 'Join Group';
28
+ });
29
+
30
+ if (!joinBtn) {
31
+ const isMember = buttons.some(b => {
32
+ const t = b.textContent.trim();
33
+ return t === '已加入' || t === 'Joined' || t === '成员' || t === 'Member';
34
+ });
35
+ if (isMember) return [{ status: 'Already a member', group: groupName }];
36
+ return [{ status: 'Join button not found', group: groupName }];
37
+ }
38
+
39
+ joinBtn.click();
40
+ await new Promise(r => setTimeout(r, 1500));
41
+ return [{ status: 'Join request sent', group: groupName }];
42
+ })()
43
+
44
+ columns: [status, group]
@@ -0,0 +1,39 @@
1
+ site: facebook
2
+ name: memories
3
+ description: Get your Facebook memories (On This Day)
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ limit:
8
+ type: int
9
+ default: 10
10
+ description: Number of memories
11
+
12
+ pipeline:
13
+ - navigate:
14
+ url: https://www.facebook.com/onthisday
15
+ settleMs: 4000
16
+
17
+ - evaluate: |
18
+ (() => {
19
+ const limit = ${{ args.limit }};
20
+ const articles = document.querySelectorAll('[role="article"]');
21
+ return Array.from(articles)
22
+ .slice(0, limit)
23
+ .map((el, i) => {
24
+ const headerLink = el.querySelector('h2 a, h3 a, h4 a, strong a');
25
+ const spans = Array.from(el.querySelectorAll('div[dir="auto"]'))
26
+ .map(s => s.textContent.trim())
27
+ .filter(t => t.length > 5 && t.length < 500);
28
+ const timeEl = el.querySelector('a[href*="/posts/"] span, a[href*="story_fbid"] span');
29
+ return {
30
+ index: i + 1,
31
+ source: headerLink ? headerLink.textContent.trim().substring(0, 50) : '-',
32
+ content: (spans[0] || '').replace(/\n/g, ' ').substring(0, 150),
33
+ time: timeEl ? timeEl.textContent.trim() : '-',
34
+ };
35
+ })
36
+ .filter(item => item.content.length > 0 || item.source !== '-');
37
+ })()
38
+
39
+ columns: [index, source, content, time]
@@ -0,0 +1,40 @@
1
+ site: facebook
2
+ name: notifications
3
+ description: Get recent Facebook notifications
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ limit:
8
+ type: int
9
+ default: 15
10
+ description: Number of notifications
11
+
12
+ pipeline:
13
+ - navigate:
14
+ url: https://www.facebook.com/notifications
15
+ settleMs: 3000
16
+
17
+ - evaluate: |
18
+ (() => {
19
+ const limit = ${{ args.limit }};
20
+ const items = document.querySelectorAll('[role="listitem"]');
21
+ return Array.from(items)
22
+ .filter(el => el.querySelectorAll('a').length > 0)
23
+ .slice(0, limit)
24
+ .map((el, i) => {
25
+ const raw = el.textContent.trim().replace(/\s+/g, ' ');
26
+ // Remove leading "未读" and trailing "标记为已读"
27
+ const cleaned = raw.replace(/^未读/, '').replace(/标记为已读$/, '').replace(/^Unread/, '').replace(/Mark as read$/, '').trim();
28
+ // Try to extract time (last segment like "11小时", "5天", "1周")
29
+ const timeMatch = cleaned.match(/(\d+\s*(?:分钟|小时|天|周|个月|minutes?|hours?|days?|weeks?|months?))\s*$/);
30
+ const time = timeMatch ? timeMatch[1] : '';
31
+ const text = timeMatch ? cleaned.slice(0, -timeMatch[0].length).trim() : cleaned;
32
+ return {
33
+ index: i + 1,
34
+ text: text.substring(0, 150),
35
+ time: time || '-',
36
+ };
37
+ });
38
+ })()
39
+
40
+ columns: [index, text, time]
@@ -0,0 +1,37 @@
1
+ site: facebook
2
+ name: profile
3
+ description: Get Facebook user/page profile info
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ username:
8
+ type: str
9
+ required: true
10
+ positional: true
11
+ description: Facebook username or page name
12
+
13
+ pipeline:
14
+ - navigate:
15
+ url: https://www.facebook.com/${{ args.username }}
16
+ settleMs: 3000
17
+
18
+ - evaluate: |
19
+ (() => {
20
+ const h1 = document.querySelector('h1');
21
+ let name = h1 ? h1.textContent.trim() : '';
22
+
23
+ // Find friends/followers links
24
+ const links = Array.from(document.querySelectorAll('a'));
25
+ const friendsLink = links.find(a => a.href && a.href.includes('/friends'));
26
+ const followersLink = links.find(a => a.href && a.href.includes('/followers'));
27
+
28
+ return [{
29
+ name: name,
30
+ username: ${{ args.username | json }},
31
+ friends: friendsLink ? friendsLink.textContent.trim() : '-',
32
+ followers: followersLink ? followersLink.textContent.trim() : '-',
33
+ url: window.location.href,
34
+ }];
35
+ })()
36
+
37
+ columns: [name, username, friends, followers, url]
@@ -0,0 +1,46 @@
1
+ site: facebook
2
+ name: search
3
+ description: Search Facebook for people, pages, or posts
4
+ domain: www.facebook.com
5
+
6
+ args:
7
+ query:
8
+ type: str
9
+ required: true
10
+ positional: true
11
+ description: Search query
12
+ limit:
13
+ type: int
14
+ default: 10
15
+ description: Number of results
16
+
17
+ pipeline:
18
+ - navigate: https://www.facebook.com
19
+
20
+ - evaluate: |
21
+ (async () => {
22
+ const query = ${{ args.query | json }};
23
+ const limit = ${{ args.limit }};
24
+ window.location.href = 'https://www.facebook.com/search/top?q=' + encodeURIComponent(query);
25
+ await new Promise(r => setTimeout(r, 4000));
26
+ // Search results are typically in role="article" or role="listitem"
27
+ let items = document.querySelectorAll('[role="article"]');
28
+ if (items.length === 0) {
29
+ items = document.querySelectorAll('[role="listitem"]');
30
+ }
31
+ return Array.from(items)
32
+ .filter(el => el.textContent.trim().length > 20)
33
+ .slice(0, limit)
34
+ .map((el, i) => {
35
+ const link = el.querySelector('a[href*="facebook.com/"]');
36
+ const heading = el.querySelector('h2, h3, h4, strong');
37
+ return {
38
+ index: i + 1,
39
+ title: heading ? heading.textContent.trim().substring(0, 80) : '',
40
+ text: el.textContent.trim().replace(/\s+/g, ' ').substring(0, 150),
41
+ url: link ? link.href.split('?')[0] : '',
42
+ };
43
+ });
44
+ })()
45
+
46
+ columns: [index, title, text, url]
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Google News via public RSS feed.
3
+ * Supports top stories (no keyword) and search (with keyword).
4
+ */
5
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Google News via public RSS feed.
3
+ * Supports top stories (no keyword) and search (with keyword).
4
+ */
5
+ import { cli, Strategy } from '../../registry.js';
6
+ import { CliError } from '../../errors.js';
7
+ import { parseRssItems } from './utils.js';
8
+ cli({
9
+ site: 'google',
10
+ name: 'news',
11
+ description: 'Get Google News headlines',
12
+ strategy: Strategy.PUBLIC,
13
+ browser: false,
14
+ args: [
15
+ { name: 'keyword', positional: true, help: 'Search query (omit for top stories)' },
16
+ { name: 'limit', type: 'int', default: 10, help: 'Number of results' },
17
+ { name: 'lang', default: 'en', help: 'Language short code (e.g. en, zh)' },
18
+ { name: 'region', default: 'US', help: 'Region code (e.g. US, CN)' },
19
+ ],
20
+ columns: ['title', 'source', 'date', 'url'],
21
+ func: async (_page, args) => {
22
+ const limit = Math.max(1, Math.min(Number(args.limit), 100));
23
+ const lang = encodeURIComponent(args.lang);
24
+ const region = encodeURIComponent(args.region);
25
+ const ceid = `${args.region}:${args.lang}`;
26
+ // Top stories or search
27
+ const base = args.keyword
28
+ ? `https://news.google.com/rss/search?q=${encodeURIComponent(args.keyword)}&hl=${lang}&gl=${region}&ceid=${ceid}`
29
+ : `https://news.google.com/rss?hl=${lang}&gl=${region}&ceid=${ceid}`;
30
+ const resp = await fetch(base);
31
+ if (!resp.ok) {
32
+ throw new CliError('FETCH_ERROR', `HTTP ${resp.status}`, 'Check your network connection');
33
+ }
34
+ const xml = await resp.text();
35
+ const items = parseRssItems(xml, ['title', 'link', 'pubDate', 'source']);
36
+ if (!items.length) {
37
+ throw new CliError('NOT_FOUND', 'No news articles found', 'Try a different keyword or region');
38
+ }
39
+ return items.slice(0, limit).map(item => {
40
+ // Extract source: prefer <source> element, fallback to parsing title
41
+ let title = item['title'] || '';
42
+ let source = item['source'] || '';
43
+ if (!source) {
44
+ const idx = title.lastIndexOf(' - ');
45
+ if (idx !== -1) {
46
+ source = title.slice(idx + 3);
47
+ title = title.slice(0, idx);
48
+ }
49
+ }
50
+ return {
51
+ title,
52
+ source,
53
+ date: item['pubDate'] || '',
54
+ url: item['link'] || '',
55
+ };
56
+ });
57
+ },
58
+ });