@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.
- package/CONTRIBUTING.md +39 -1
- package/README.md +9 -10
- package/README.zh-CN.md +39 -17
- package/SKILL.md +10 -5
- package/dist/browser/cdp.d.ts +4 -4
- package/dist/browser/cdp.js +39 -16
- package/dist/browser/daemon-client.d.ts +2 -1
- package/dist/browser/dom-helpers.js +38 -7
- package/dist/browser/dom-snapshot.d.ts +86 -0
- package/dist/browser/dom-snapshot.js +729 -0
- package/dist/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/browser/dom-snapshot.test.js +212 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/page.d.ts +14 -24
- package/dist/browser/page.js +37 -4
- package/dist/build-manifest.d.ts +11 -4
- package/dist/build-manifest.js +59 -21
- package/dist/build-manifest.test.js +58 -2
- package/dist/cli-manifest.json +3856 -1509
- package/dist/cli.js +66 -0
- package/dist/clis/barchart/greeks.js +1 -1
- package/dist/clis/barchart/options.js +1 -1
- package/dist/clis/barchart/quote.js +1 -1
- package/dist/clis/bilibili/download.js +1 -1
- package/dist/clis/bilibili/following.js +1 -1
- package/dist/clis/bilibili/subtitle.js +1 -1
- package/dist/clis/bilibili/user-videos.js +1 -1
- package/dist/clis/boss/batchgreet.js +10 -97
- package/dist/clis/boss/chatlist.js +8 -25
- package/dist/clis/boss/chatmsg.js +11 -42
- package/dist/clis/boss/common.d.ts +92 -0
- package/dist/clis/boss/common.js +223 -0
- package/dist/clis/boss/detail.js +7 -49
- package/dist/clis/boss/exchange.js +13 -79
- package/dist/clis/boss/greet.js +18 -145
- package/dist/clis/boss/invite.js +26 -121
- package/dist/clis/boss/joblist.js +6 -31
- package/dist/clis/boss/mark.js +12 -85
- package/dist/clis/boss/recommend.js +10 -49
- package/dist/clis/boss/resume.js +18 -118
- package/dist/clis/boss/search.js +12 -60
- package/dist/clis/boss/send.js +17 -151
- package/dist/clis/boss/stats.js +18 -69
- package/dist/clis/coupang/add-to-cart.js +1 -1
- package/dist/clis/devto/tag.yaml +34 -0
- package/dist/clis/devto/top.yaml +29 -0
- package/dist/clis/devto/user.yaml +33 -0
- package/dist/clis/douban/book-hot.d.ts +1 -0
- package/dist/clis/douban/book-hot.js +14 -0
- package/dist/clis/douban/marks.d.ts +1 -0
- package/dist/clis/douban/marks.js +115 -0
- package/dist/clis/douban/movie-hot.d.ts +1 -0
- package/dist/clis/douban/movie-hot.js +14 -0
- package/dist/clis/douban/reviews.d.ts +1 -0
- package/dist/clis/douban/reviews.js +106 -0
- package/dist/clis/douban/search.d.ts +1 -0
- package/dist/clis/douban/search.js +16 -0
- package/dist/clis/douban/shared.d.ts +4 -0
- package/dist/clis/douban/shared.js +155 -0
- package/dist/clis/douban/subject.yaml +76 -0
- package/dist/clis/douban/top250.yaml +70 -0
- package/dist/clis/douban/utils.d.ts +35 -0
- package/dist/clis/douban/utils.js +48 -0
- package/dist/clis/facebook/add-friend.yaml +43 -0
- package/dist/clis/facebook/events.yaml +44 -0
- package/dist/clis/facebook/feed.yaml +63 -0
- package/dist/clis/facebook/friends.yaml +42 -0
- package/dist/clis/facebook/groups.yaml +50 -0
- package/dist/clis/facebook/join-group.yaml +44 -0
- package/dist/clis/facebook/memories.yaml +39 -0
- package/dist/clis/facebook/notifications.yaml +40 -0
- package/dist/clis/facebook/profile.yaml +37 -0
- package/dist/clis/facebook/search.yaml +46 -0
- package/dist/clis/google/news.d.ts +5 -0
- package/dist/clis/google/news.js +58 -0
- package/dist/clis/google/search.d.ts +10 -0
- package/dist/clis/google/search.js +127 -0
- package/dist/clis/google/suggest.d.ts +5 -0
- package/dist/clis/google/suggest.js +34 -0
- package/dist/clis/google/trends.d.ts +5 -0
- package/dist/clis/google/trends.js +38 -0
- package/dist/clis/google/utils.d.ts +9 -0
- package/dist/clis/google/utils.js +23 -0
- package/dist/clis/google/utils.test.d.ts +1 -0
- package/dist/clis/google/utils.test.js +75 -0
- package/dist/clis/grok/ask.d.ts +14 -0
- package/dist/clis/grok/ask.js +257 -65
- package/dist/clis/grok/ask.test.d.ts +1 -0
- package/dist/clis/grok/ask.test.js +36 -0
- package/dist/clis/instagram/comment.yaml +52 -0
- package/dist/clis/instagram/explore.yaml +43 -0
- package/dist/clis/instagram/follow.yaml +41 -0
- package/dist/clis/instagram/followers.yaml +51 -0
- package/dist/clis/instagram/following.yaml +51 -0
- package/dist/clis/instagram/like.yaml +46 -0
- package/dist/clis/instagram/profile.yaml +42 -0
- package/dist/clis/instagram/save.yaml +46 -0
- package/dist/clis/instagram/saved.yaml +40 -0
- package/dist/clis/instagram/search.yaml +43 -0
- package/dist/clis/instagram/unfollow.yaml +38 -0
- package/dist/clis/instagram/unlike.yaml +46 -0
- package/dist/clis/instagram/unsave.yaml +46 -0
- package/dist/clis/instagram/user.yaml +54 -0
- package/dist/clis/jike/repost.js +1 -1
- package/dist/clis/jimeng/generate.yaml +1 -0
- package/dist/clis/linux-do/category.yaml +1 -0
- package/dist/clis/lobsters/active.yaml +29 -0
- package/dist/clis/lobsters/hot.yaml +29 -0
- package/dist/clis/lobsters/newest.yaml +29 -0
- package/dist/clis/lobsters/tag.yaml +34 -0
- package/dist/clis/medium/feed.d.ts +1 -0
- package/dist/clis/medium/feed.js +15 -0
- package/dist/clis/medium/search.d.ts +1 -0
- package/dist/clis/medium/search.js +15 -0
- package/dist/clis/medium/shared.d.ts +5 -0
- package/dist/clis/medium/shared.js +78 -0
- package/dist/clis/medium/user.d.ts +1 -0
- package/dist/clis/medium/user.js +15 -0
- package/dist/clis/reddit/comment.js +1 -1
- package/dist/clis/reddit/read.js +1 -1
- package/dist/clis/reddit/save.js +1 -1
- package/dist/clis/reddit/subreddit.yaml +1 -0
- package/dist/clis/reddit/subscribe.js +1 -1
- package/dist/clis/reddit/upvote.js +1 -1
- package/dist/clis/sinablog/article.d.ts +1 -0
- package/dist/clis/sinablog/article.js +14 -0
- package/dist/clis/sinablog/hot.d.ts +1 -0
- package/dist/clis/sinablog/hot.js +14 -0
- package/dist/clis/sinablog/search.d.ts +1 -0
- package/dist/clis/sinablog/search.js +51 -0
- package/dist/clis/sinablog/shared.d.ts +7 -0
- package/dist/clis/sinablog/shared.js +187 -0
- package/dist/clis/sinablog/user.d.ts +1 -0
- package/dist/clis/sinablog/user.js +15 -0
- package/dist/clis/substack/feed.d.ts +1 -0
- package/dist/clis/substack/feed.js +15 -0
- package/dist/clis/substack/publication.d.ts +1 -0
- package/dist/clis/substack/publication.js +15 -0
- package/dist/clis/substack/search.d.ts +1 -0
- package/dist/clis/substack/search.js +77 -0
- package/dist/clis/substack/shared.d.ts +4 -0
- package/dist/clis/substack/shared.js +129 -0
- package/dist/clis/tiktok/comment.yaml +66 -0
- package/dist/clis/tiktok/explore.yaml +39 -0
- package/dist/clis/tiktok/follow.yaml +39 -0
- package/dist/clis/tiktok/following.yaml +46 -0
- package/dist/clis/tiktok/friends.yaml +47 -0
- package/dist/clis/tiktok/like.yaml +38 -0
- package/dist/clis/tiktok/live.yaml +51 -0
- package/dist/clis/tiktok/notifications.yaml +52 -0
- package/dist/clis/tiktok/profile.yaml +45 -0
- package/dist/clis/tiktok/save.yaml +34 -0
- package/dist/clis/tiktok/search.yaml +46 -0
- package/dist/clis/tiktok/unfollow.yaml +44 -0
- package/dist/clis/tiktok/unlike.yaml +38 -0
- package/dist/clis/tiktok/unsave.yaml +36 -0
- package/dist/clis/tiktok/user.yaml +44 -0
- package/dist/clis/twitter/download.d.ts +1 -1
- package/dist/clis/twitter/download.js +3 -3
- package/dist/clis/twitter/followers.js +1 -1
- package/dist/clis/twitter/following.js +1 -1
- package/dist/clis/twitter/thread.js +1 -1
- package/dist/clis/twitter/timeline.d.ts +23 -0
- package/dist/clis/twitter/timeline.js +42 -14
- package/dist/clis/twitter/timeline.test.d.ts +1 -0
- package/dist/clis/twitter/timeline.test.js +102 -0
- package/dist/clis/wikipedia/random.d.ts +1 -0
- package/dist/clis/wikipedia/random.js +19 -0
- package/dist/clis/wikipedia/search.js +3 -3
- package/dist/clis/wikipedia/summary.js +4 -9
- package/dist/clis/wikipedia/trending.d.ts +1 -0
- package/dist/clis/wikipedia/trending.js +35 -0
- package/dist/clis/wikipedia/utils.d.ts +28 -0
- package/dist/clis/wikipedia/utils.js +13 -0
- package/dist/clis/xiaohongshu/creator-note-detail.js +1 -1
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +2 -0
- package/dist/clis/xiaohongshu/creator-notes.test.js +2 -0
- package/dist/clis/xiaohongshu/download.js +1 -1
- package/dist/clis/xueqiu/earnings-date.yaml +69 -0
- package/dist/clis/xueqiu/search.yaml +2 -1
- package/dist/clis/xueqiu/stock.yaml +2 -0
- package/dist/clis/yahoo-finance/quote.js +1 -1
- package/dist/commanderAdapter.js +13 -7
- package/dist/discovery.d.ts +8 -0
- package/dist/discovery.js +105 -19
- package/dist/doctor.js +3 -1
- package/dist/doctor.test.js +46 -2
- package/dist/engine.test.d.ts +0 -3
- package/dist/engine.test.js +74 -6
- package/dist/execution.d.ts +4 -2
- package/dist/execution.js +31 -7
- package/dist/explore.d.ts +76 -3
- package/dist/explore.js +11 -4
- package/dist/generate.d.ts +41 -2
- package/dist/generate.js +5 -4
- package/dist/main.js +2 -1
- package/dist/pipeline/executor.d.ts +2 -2
- package/dist/pipeline/executor.js +2 -2
- package/dist/pipeline/executor.test.js +33 -6
- package/dist/pipeline/registry.d.ts +1 -1
- package/dist/pipeline/steps/browser.d.ts +7 -7
- package/dist/pipeline/steps/browser.js +15 -7
- package/dist/pipeline/steps/fetch.d.ts +1 -1
- package/dist/pipeline/steps/fetch.js +11 -7
- package/dist/pipeline/steps/transform.d.ts +6 -5
- package/dist/pipeline/steps/transform.js +30 -9
- package/dist/pipeline/template.d.ts +6 -6
- package/dist/pipeline/template.js +43 -5
- package/dist/pipeline/template.test.js +18 -0
- package/dist/pipeline/transform.test.js +11 -0
- package/dist/plugin.d.ts +31 -0
- package/dist/plugin.js +216 -0
- package/dist/plugin.test.d.ts +4 -0
- package/dist/plugin.test.js +76 -0
- package/dist/registry-api.d.ts +11 -0
- package/dist/registry-api.js +9 -0
- package/dist/registry.d.ts +11 -0
- package/dist/registry.js +6 -1
- package/dist/synthesize.d.ts +94 -4
- package/dist/synthesize.js +5 -4
- package/dist/types.d.ts +39 -26
- package/dist/validate.js +8 -2
- package/docs/.vitepress/config.mts +6 -4
- package/docs/adapters/browser/barchart.md +6 -5
- package/docs/adapters/browser/bilibili.md +9 -0
- package/docs/adapters/browser/devto.md +35 -0
- package/docs/adapters/browser/douban.md +38 -0
- package/docs/adapters/browser/facebook.md +36 -0
- package/docs/adapters/browser/google.md +62 -0
- package/docs/adapters/browser/grok.md +26 -8
- package/docs/adapters/browser/instagram.md +46 -0
- package/docs/adapters/browser/lobsters.md +32 -0
- package/docs/adapters/browser/medium.md +32 -0
- package/docs/adapters/browser/reddit.md +9 -0
- package/docs/adapters/browser/sinablog.md +36 -0
- package/docs/adapters/browser/substack.md +38 -0
- package/docs/adapters/browser/tiktok.md +68 -0
- package/docs/adapters/browser/wikipedia.md +11 -2
- package/docs/adapters/browser/xueqiu.md +10 -0
- package/docs/adapters/browser/yahoo-finance.md +6 -5
- package/docs/adapters/desktop/antigravity.md +6 -0
- package/docs/adapters/desktop/chatgpt.md +2 -1
- package/docs/adapters/desktop/codex.md +5 -1
- package/docs/adapters/desktop/cursor.md +4 -0
- package/docs/adapters/desktop/discord.md +7 -7
- package/docs/adapters/index.md +1 -4
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +153 -0
- package/docs/zh/guide/plugins.md +107 -0
- package/extension/src/background.ts +18 -11
- package/package.json +10 -5
- package/scripts/clean-dist.cjs +13 -0
- package/src/browser/cdp.ts +71 -31
- package/src/browser/daemon-client.ts +2 -1
- package/src/browser/dom-helpers.ts +38 -7
- package/src/browser/dom-snapshot.test.ts +249 -0
- package/src/browser/dom-snapshot.ts +770 -0
- package/src/browser/index.ts +2 -0
- package/src/browser/page.ts +50 -19
- package/src/build-manifest.test.ts +70 -2
- package/src/build-manifest.ts +94 -26
- package/src/cli.ts +71 -2
- package/src/clis/barchart/greeks.ts +1 -1
- package/src/clis/barchart/options.ts +1 -1
- package/src/clis/barchart/quote.ts +1 -1
- package/src/clis/bilibili/download.ts +1 -1
- package/src/clis/bilibili/following.ts +1 -1
- package/src/clis/bilibili/subtitle.ts +1 -1
- package/src/clis/bilibili/user-videos.ts +1 -1
- package/src/clis/boss/batchgreet.ts +14 -106
- package/src/clis/boss/chatlist.ts +12 -26
- package/src/clis/boss/chatmsg.ts +16 -40
- package/src/clis/boss/common.ts +287 -0
- package/src/clis/boss/detail.ts +8 -54
- package/src/clis/boss/exchange.ts +15 -89
- package/src/clis/boss/greet.ts +23 -160
- package/src/clis/boss/invite.ts +36 -133
- package/src/clis/boss/joblist.ts +7 -36
- package/src/clis/boss/mark.ts +13 -94
- package/src/clis/boss/recommend.ts +12 -57
- package/src/clis/boss/resume.ts +19 -124
- package/src/clis/boss/search.ts +13 -66
- package/src/clis/boss/send.ts +21 -161
- package/src/clis/boss/stats.ts +19 -74
- package/src/clis/coupang/add-to-cart.ts +1 -1
- package/src/clis/devto/tag.yaml +34 -0
- package/src/clis/devto/top.yaml +29 -0
- package/src/clis/devto/user.yaml +33 -0
- package/src/clis/douban/book-hot.ts +15 -0
- package/src/clis/douban/marks.ts +135 -0
- package/src/clis/douban/movie-hot.ts +15 -0
- package/src/clis/douban/reviews.ts +127 -0
- package/src/clis/douban/search.ts +17 -0
- package/src/clis/douban/shared.ts +165 -0
- package/src/clis/douban/subject.yaml +76 -0
- package/src/clis/douban/top250.yaml +70 -0
- package/src/clis/douban/utils.ts +81 -0
- package/src/clis/facebook/add-friend.yaml +43 -0
- package/src/clis/facebook/events.yaml +44 -0
- package/src/clis/facebook/feed.yaml +63 -0
- package/src/clis/facebook/friends.yaml +42 -0
- package/src/clis/facebook/groups.yaml +50 -0
- package/src/clis/facebook/join-group.yaml +44 -0
- package/src/clis/facebook/memories.yaml +39 -0
- package/src/clis/facebook/notifications.yaml +40 -0
- package/src/clis/facebook/profile.yaml +37 -0
- package/src/clis/facebook/search.yaml +46 -0
- package/src/clis/google/news.ts +66 -0
- package/src/clis/google/search.ts +133 -0
- package/src/clis/google/suggest.ts +40 -0
- package/src/clis/google/trends.ts +44 -0
- package/src/clis/google/utils.test.ts +82 -0
- package/src/clis/google/utils.ts +24 -0
- package/src/clis/grok/ask.test.ts +53 -0
- package/src/clis/grok/ask.ts +300 -69
- package/src/clis/instagram/comment.yaml +52 -0
- package/src/clis/instagram/explore.yaml +43 -0
- package/src/clis/instagram/follow.yaml +41 -0
- package/src/clis/instagram/followers.yaml +51 -0
- package/src/clis/instagram/following.yaml +51 -0
- package/src/clis/instagram/like.yaml +46 -0
- package/src/clis/instagram/profile.yaml +42 -0
- package/src/clis/instagram/save.yaml +46 -0
- package/src/clis/instagram/saved.yaml +40 -0
- package/src/clis/instagram/search.yaml +43 -0
- package/src/clis/instagram/unfollow.yaml +38 -0
- package/src/clis/instagram/unlike.yaml +46 -0
- package/src/clis/instagram/unsave.yaml +46 -0
- package/src/clis/instagram/user.yaml +54 -0
- package/src/clis/jike/repost.ts +1 -1
- package/src/clis/jimeng/generate.yaml +1 -0
- package/src/clis/linux-do/category.yaml +1 -0
- package/src/clis/lobsters/active.yaml +29 -0
- package/src/clis/lobsters/hot.yaml +29 -0
- package/src/clis/lobsters/newest.yaml +29 -0
- package/src/clis/lobsters/tag.yaml +34 -0
- package/src/clis/medium/feed.ts +16 -0
- package/src/clis/medium/search.ts +16 -0
- package/src/clis/medium/shared.ts +83 -0
- package/src/clis/medium/user.ts +16 -0
- package/src/clis/reddit/comment.ts +1 -1
- package/src/clis/reddit/read.ts +1 -1
- package/src/clis/reddit/save.ts +1 -1
- package/src/clis/reddit/subreddit.yaml +1 -0
- package/src/clis/reddit/subscribe.ts +1 -1
- package/src/clis/reddit/upvote.ts +1 -1
- package/src/clis/sinablog/article.ts +15 -0
- package/src/clis/sinablog/hot.ts +15 -0
- package/src/clis/sinablog/search.ts +56 -0
- package/src/clis/sinablog/shared.ts +198 -0
- package/src/clis/sinablog/user.ts +16 -0
- package/src/clis/substack/feed.ts +16 -0
- package/src/clis/substack/publication.ts +16 -0
- package/src/clis/substack/search.ts +91 -0
- package/src/clis/substack/shared.ts +132 -0
- package/src/clis/tiktok/comment.yaml +66 -0
- package/src/clis/tiktok/explore.yaml +39 -0
- package/src/clis/tiktok/follow.yaml +39 -0
- package/src/clis/tiktok/following.yaml +46 -0
- package/src/clis/tiktok/friends.yaml +47 -0
- package/src/clis/tiktok/like.yaml +38 -0
- package/src/clis/tiktok/live.yaml +51 -0
- package/src/clis/tiktok/notifications.yaml +52 -0
- package/src/clis/tiktok/profile.yaml +45 -0
- package/src/clis/tiktok/save.yaml +34 -0
- package/src/clis/tiktok/search.yaml +46 -0
- package/src/clis/tiktok/unfollow.yaml +44 -0
- package/src/clis/tiktok/unlike.yaml +38 -0
- package/src/clis/tiktok/unsave.yaml +36 -0
- package/src/clis/tiktok/user.yaml +44 -0
- package/src/clis/twitter/download.ts +3 -3
- package/src/clis/twitter/followers.ts +1 -1
- package/src/clis/twitter/following.ts +1 -1
- package/src/clis/twitter/thread.ts +1 -1
- package/src/clis/twitter/timeline.test.ts +109 -0
- package/src/clis/twitter/timeline.ts +59 -19
- package/src/clis/wikipedia/random.ts +19 -0
- package/src/clis/wikipedia/search.ts +10 -4
- package/src/clis/wikipedia/summary.ts +4 -9
- package/src/clis/wikipedia/trending.ts +41 -0
- package/src/clis/wikipedia/utils.ts +31 -0
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +2 -0
- package/src/clis/xiaohongshu/creator-note-detail.ts +1 -1
- package/src/clis/xiaohongshu/creator-notes.test.ts +2 -0
- package/src/clis/xiaohongshu/download.ts +1 -1
- package/src/clis/xueqiu/earnings-date.yaml +69 -0
- package/src/clis/xueqiu/search.yaml +2 -1
- package/src/clis/xueqiu/stock.yaml +2 -0
- package/src/clis/yahoo-finance/quote.ts +1 -1
- package/src/commanderAdapter.ts +17 -10
- package/src/discovery.ts +134 -24
- package/src/doctor.test.ts +59 -2
- package/src/doctor.ts +4 -2
- package/src/engine.test.ts +79 -6
- package/src/execution.ts +42 -16
- package/src/explore.ts +77 -9
- package/src/generate.ts +58 -9
- package/src/main.ts +2 -1
- package/src/pipeline/executor.test.ts +35 -6
- package/src/pipeline/executor.ts +11 -7
- package/src/pipeline/registry.ts +3 -3
- package/src/pipeline/steps/browser.ts +24 -15
- package/src/pipeline/steps/fetch.ts +18 -13
- package/src/pipeline/steps/transform.ts +40 -15
- package/src/pipeline/template.test.ts +18 -0
- package/src/pipeline/template.ts +86 -13
- package/src/pipeline/transform.test.ts +15 -2
- package/src/plugin.test.ts +86 -0
- package/src/plugin.ts +254 -0
- package/src/registry-api.ts +12 -0
- package/src/registry.ts +19 -1
- package/src/synthesize.ts +102 -21
- package/src/types.ts +44 -12
- package/src/validate.ts +19 -4
- package/tests/e2e/browser-public.test.ts +11 -0
- package/tests/e2e/public-commands.test.ts +64 -0
- package/dist/clis/feishu/new.d.ts +0 -1
- package/dist/clis/feishu/new.js +0 -27
- package/dist/clis/feishu/read.d.ts +0 -1
- package/dist/clis/feishu/read.js +0 -40
- package/dist/clis/feishu/search.d.ts +0 -1
- package/dist/clis/feishu/search.js +0 -30
- package/dist/clis/feishu/send.d.ts +0 -1
- package/dist/clis/feishu/send.js +0 -39
- package/dist/clis/feishu/status.d.ts +0 -1
- package/dist/clis/feishu/status.js +0 -28
- package/dist/clis/neteasemusic/like.d.ts +0 -1
- package/dist/clis/neteasemusic/like.js +0 -25
- package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
- package/dist/clis/neteasemusic/lyrics.js +0 -47
- package/dist/clis/neteasemusic/next.d.ts +0 -1
- package/dist/clis/neteasemusic/next.js +0 -26
- package/dist/clis/neteasemusic/play.d.ts +0 -1
- package/dist/clis/neteasemusic/play.js +0 -26
- package/dist/clis/neteasemusic/playing.d.ts +0 -1
- package/dist/clis/neteasemusic/playing.js +0 -59
- package/dist/clis/neteasemusic/playlist.d.ts +0 -1
- package/dist/clis/neteasemusic/playlist.js +0 -46
- package/dist/clis/neteasemusic/prev.d.ts +0 -1
- package/dist/clis/neteasemusic/prev.js +0 -25
- package/dist/clis/neteasemusic/search.d.ts +0 -1
- package/dist/clis/neteasemusic/search.js +0 -52
- package/dist/clis/neteasemusic/status.d.ts +0 -1
- package/dist/clis/neteasemusic/status.js +0 -16
- package/dist/clis/neteasemusic/volume.d.ts +0 -1
- package/dist/clis/neteasemusic/volume.js +0 -54
- package/dist/clis/wechat/chats.d.ts +0 -1
- package/dist/clis/wechat/chats.js +0 -28
- package/dist/clis/wechat/contacts.d.ts +0 -1
- package/dist/clis/wechat/contacts.js +0 -28
- package/dist/clis/wechat/read.d.ts +0 -1
- package/dist/clis/wechat/read.js +0 -58
- package/dist/clis/wechat/search.d.ts +0 -1
- package/dist/clis/wechat/search.js +0 -31
- package/dist/clis/wechat/send.d.ts +0 -1
- package/dist/clis/wechat/send.js +0 -42
- package/dist/clis/wechat/status.d.ts +0 -1
- package/dist/clis/wechat/status.js +0 -29
- package/dist/pipeline.d.ts +0 -7
- package/dist/pipeline.js +0 -7
- package/docs/adapters/browser/github.md +0 -26
- package/docs/adapters/desktop/feishu.md +0 -20
- package/docs/adapters/desktop/neteasemusic.md +0 -31
- package/docs/adapters/desktop/wechat.md +0 -28
- package/src/clis/antigravity/README.md +0 -5
- package/src/clis/antigravity/README.zh-CN.md +0 -51
- package/src/clis/chaoxing/README.md +0 -14
- package/src/clis/chaoxing/README.zh-CN.md +0 -35
- package/src/clis/chatgpt/README.md +0 -5
- package/src/clis/chatgpt/README.zh-CN.md +0 -44
- package/src/clis/chatwise/README.md +0 -5
- package/src/clis/chatwise/README.zh-CN.md +0 -38
- package/src/clis/codex/README.md +0 -5
- package/src/clis/codex/README.zh-CN.md +0 -33
- package/src/clis/cursor/README.md +0 -5
- package/src/clis/cursor/README.zh-CN.md +0 -33
- package/src/clis/discord-app/README.md +0 -5
- package/src/clis/discord-app/README.zh-CN.md +0 -28
- package/src/clis/feishu/README.md +0 -5
- package/src/clis/feishu/README.zh-CN.md +0 -20
- package/src/clis/feishu/new.ts +0 -32
- package/src/clis/feishu/read.ts +0 -48
- package/src/clis/feishu/search.ts +0 -35
- package/src/clis/feishu/send.ts +0 -46
- package/src/clis/feishu/status.ts +0 -34
- package/src/clis/neteasemusic/README.md +0 -5
- package/src/clis/neteasemusic/README.zh-CN.md +0 -31
- package/src/clis/neteasemusic/like.ts +0 -28
- package/src/clis/neteasemusic/lyrics.ts +0 -53
- package/src/clis/neteasemusic/next.ts +0 -30
- package/src/clis/neteasemusic/play.ts +0 -30
- package/src/clis/neteasemusic/playing.ts +0 -62
- package/src/clis/neteasemusic/playlist.ts +0 -51
- package/src/clis/neteasemusic/prev.ts +0 -29
- package/src/clis/neteasemusic/search.ts +0 -58
- package/src/clis/neteasemusic/status.ts +0 -18
- package/src/clis/neteasemusic/volume.ts +0 -61
- package/src/clis/notion/README.md +0 -5
- package/src/clis/notion/README.zh-CN.md +0 -29
- package/src/clis/wechat/README.md +0 -5
- package/src/clis/wechat/README.zh-CN.md +0 -28
- package/src/clis/wechat/chats.ts +0 -33
- package/src/clis/wechat/contacts.ts +0 -33
- package/src/clis/wechat/read.ts +0 -72
- package/src/clis/wechat/search.ts +0 -36
- package/src/clis/wechat/send.ts +0 -49
- package/src/clis/wechat/status.ts +0 -35
- package/src/pipeline.ts +0 -8
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Twitter/X download — download images and videos from tweets.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
* opencli twitter download
|
|
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
|
export {};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Twitter/X download — download images and videos from tweets.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
* opencli twitter download
|
|
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
|
import * as fs from 'node:fs';
|
|
@@ -17,7 +17,7 @@ cli({
|
|
|
17
17
|
domain: 'x.com',
|
|
18
18
|
strategy: Strategy.COOKIE,
|
|
19
19
|
args: [
|
|
20
|
-
{ name: 'username', help: 'Twitter username (downloads from media tab)' },
|
|
20
|
+
{ name: 'username', positional: true, help: 'Twitter username (downloads from media tab)' },
|
|
21
21
|
{ name: 'tweet-url', help: 'Single tweet URL to download' },
|
|
22
22
|
{ name: 'limit', type: 'int', default: 10, help: 'Number of tweets to scan' },
|
|
23
23
|
{ name: 'output', default: './twitter-downloads', help: 'Output directory' },
|
|
@@ -33,7 +33,7 @@ cli({
|
|
|
33
33
|
index: 0,
|
|
34
34
|
type: '-',
|
|
35
35
|
status: 'failed',
|
|
36
|
-
size: 'Must provide
|
|
36
|
+
size: 'Must provide a username or --tweet-url',
|
|
37
37
|
}];
|
|
38
38
|
}
|
|
39
39
|
// Navigate to the appropriate page
|
|
@@ -7,7 +7,7 @@ cli({
|
|
|
7
7
|
strategy: Strategy.INTERCEPT,
|
|
8
8
|
browser: true,
|
|
9
9
|
args: [
|
|
10
|
-
{ name: 'user', type: 'string', required: false },
|
|
10
|
+
{ name: 'user', positional: true, type: 'string', required: false },
|
|
11
11
|
{ name: 'limit', type: 'int', default: 50 },
|
|
12
12
|
],
|
|
13
13
|
columns: ['screen_name', 'name', 'bio', 'followers'],
|
|
@@ -7,7 +7,7 @@ cli({
|
|
|
7
7
|
strategy: Strategy.INTERCEPT,
|
|
8
8
|
browser: true,
|
|
9
9
|
args: [
|
|
10
|
-
{ name: 'user', type: 'string', required: false },
|
|
10
|
+
{ name: 'user', positional: true, type: 'string', required: false },
|
|
11
11
|
{ name: 'limit', type: 'int', default: 50 },
|
|
12
12
|
],
|
|
13
13
|
columns: ['screen_name', 'name', 'bio', 'followers'],
|
|
@@ -97,7 +97,7 @@ cli({
|
|
|
97
97
|
strategy: Strategy.COOKIE,
|
|
98
98
|
browser: true,
|
|
99
99
|
args: [
|
|
100
|
-
{ name: 'tweet-id', type: 'string', required: true },
|
|
100
|
+
{ name: 'tweet-id', positional: true, type: 'string', required: true },
|
|
101
101
|
{ name: 'limit', type: 'int', default: 50 },
|
|
102
102
|
],
|
|
103
103
|
columns: ['id', 'author', 'text', 'likes', 'retweets', 'url'],
|
|
@@ -1 +1,24 @@
|
|
|
1
|
+
type TimelineType = 'for-you' | 'following';
|
|
2
|
+
interface TimelineTweet {
|
|
3
|
+
id: string;
|
|
4
|
+
author: string;
|
|
5
|
+
text: string;
|
|
6
|
+
likes: number;
|
|
7
|
+
retweets: number;
|
|
8
|
+
replies: number;
|
|
9
|
+
views: number;
|
|
10
|
+
created_at: string;
|
|
11
|
+
url: string;
|
|
12
|
+
}
|
|
13
|
+
declare function buildTimelineVariables(type: TimelineType, count: number, cursor?: string | null): Record<string, unknown>;
|
|
14
|
+
declare function buildHomeTimelineUrl(queryId: string, endpoint: string, vars: Record<string, unknown>): string;
|
|
15
|
+
declare function parseHomeTimeline(data: any, seen: Set<string>): {
|
|
16
|
+
tweets: TimelineTweet[];
|
|
17
|
+
nextCursor: string | null;
|
|
18
|
+
};
|
|
19
|
+
export declare const __test__: {
|
|
20
|
+
buildTimelineVariables: typeof buildTimelineVariables;
|
|
21
|
+
buildHomeTimelineUrl: typeof buildHomeTimelineUrl;
|
|
22
|
+
parseHomeTimeline: typeof parseHomeTimeline;
|
|
23
|
+
};
|
|
1
24
|
export {};
|
|
@@ -2,6 +2,12 @@ import { cli, Strategy } from '../../registry.js';
|
|
|
2
2
|
// ── Twitter GraphQL constants ──────────────────────────────────────────
|
|
3
3
|
const BEARER_TOKEN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
|
|
4
4
|
const HOME_TIMELINE_QUERY_ID = 'c-CzHF1LboFilMpsx4ZCrQ';
|
|
5
|
+
const HOME_LATEST_TIMELINE_QUERY_ID = 'BKB7oi212Fi7kQtCBGE4zA';
|
|
6
|
+
// Endpoint config: for-you uses GET HomeTimeline, following uses POST HomeLatestTimeline
|
|
7
|
+
const TIMELINE_ENDPOINTS = {
|
|
8
|
+
'for-you': { endpoint: 'HomeTimeline', method: 'GET', fallbackQueryId: HOME_TIMELINE_QUERY_ID },
|
|
9
|
+
following: { endpoint: 'HomeLatestTimeline', method: 'POST', fallbackQueryId: HOME_LATEST_TIMELINE_QUERY_ID },
|
|
10
|
+
};
|
|
5
11
|
const FEATURES = {
|
|
6
12
|
rweb_video_screen_enabled: false,
|
|
7
13
|
profile_label_improvements_pcf_label_in_post_enabled: true,
|
|
@@ -35,19 +41,25 @@ const FEATURES = {
|
|
|
35
41
|
responsive_web_grok_image_annotation_enabled: true,
|
|
36
42
|
responsive_web_enhance_cards_enabled: false,
|
|
37
43
|
};
|
|
38
|
-
function
|
|
44
|
+
function buildTimelineVariables(type, count, cursor) {
|
|
39
45
|
const vars = {
|
|
40
46
|
count,
|
|
41
47
|
includePromotedContent: false,
|
|
42
48
|
latestControlAvailable: true,
|
|
43
49
|
requestContext: 'launch',
|
|
44
|
-
withCommunity: true,
|
|
45
50
|
};
|
|
51
|
+
if (type === 'for-you')
|
|
52
|
+
vars.withCommunity = true;
|
|
53
|
+
if (type === 'following')
|
|
54
|
+
vars.seenTweetIds = [];
|
|
46
55
|
if (cursor)
|
|
47
56
|
vars.cursor = cursor;
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
return vars;
|
|
58
|
+
}
|
|
59
|
+
function buildHomeTimelineUrl(queryId, endpoint, vars) {
|
|
60
|
+
return (`/i/api/graphql/${queryId}/${endpoint}` +
|
|
61
|
+
`?variables=${encodeURIComponent(JSON.stringify(vars))}` +
|
|
62
|
+
`&features=${encodeURIComponent(JSON.stringify(FEATURES))}`);
|
|
51
63
|
}
|
|
52
64
|
function extractTweet(result, seen) {
|
|
53
65
|
if (!result)
|
|
@@ -76,6 +88,7 @@ function extractTweet(result, seen) {
|
|
|
76
88
|
function parseHomeTimeline(data, seen) {
|
|
77
89
|
const tweets = [];
|
|
78
90
|
let nextCursor = null;
|
|
91
|
+
// Both HomeTimeline and HomeLatestTimeline share the same response envelope
|
|
79
92
|
const instructions = data?.data?.home?.home_timeline_urt?.instructions || [];
|
|
80
93
|
for (const inst of instructions) {
|
|
81
94
|
for (const entry of inst.entries || []) {
|
|
@@ -120,16 +133,24 @@ function parseHomeTimeline(data, seen) {
|
|
|
120
133
|
cli({
|
|
121
134
|
site: 'twitter',
|
|
122
135
|
name: 'timeline',
|
|
123
|
-
description: 'Fetch Twitter
|
|
136
|
+
description: 'Fetch Twitter timeline (for-you or following)',
|
|
124
137
|
domain: 'x.com',
|
|
125
138
|
strategy: Strategy.COOKIE,
|
|
126
139
|
browser: true,
|
|
127
140
|
args: [
|
|
141
|
+
{
|
|
142
|
+
name: 'type',
|
|
143
|
+
default: 'for-you',
|
|
144
|
+
choices: ['for-you', 'following'],
|
|
145
|
+
help: 'Timeline type: for-you (algorithmic) or following (chronological)',
|
|
146
|
+
},
|
|
128
147
|
{ name: 'limit', type: 'int', default: 20 },
|
|
129
148
|
],
|
|
130
149
|
columns: ['id', 'author', 'text', 'likes', 'retweets', 'replies', 'views', 'created_at', 'url'],
|
|
131
150
|
func: async (page, kwargs) => {
|
|
132
151
|
const limit = kwargs.limit || 20;
|
|
152
|
+
const timelineType = kwargs.type === 'following' ? 'following' : 'for-you';
|
|
153
|
+
const { endpoint, method, fallbackQueryId } = TIMELINE_ENDPOINTS[timelineType];
|
|
133
154
|
// Navigate to x.com for cookie context
|
|
134
155
|
await page.goto('https://x.com');
|
|
135
156
|
await page.wait(3);
|
|
@@ -139,21 +160,23 @@ cli({
|
|
|
139
160
|
}`);
|
|
140
161
|
if (!ct0)
|
|
141
162
|
throw new Error('Not logged into x.com (no ct0 cookie)');
|
|
142
|
-
// Dynamically resolve queryId
|
|
143
|
-
const
|
|
163
|
+
// Dynamically resolve queryId for the selected endpoint
|
|
164
|
+
const resolved = await page.evaluate(`async () => {
|
|
144
165
|
try {
|
|
145
166
|
const ghResp = await fetch('https://raw.githubusercontent.com/fa0311/twitter-openapi/refs/heads/main/src/config/placeholder.json');
|
|
146
167
|
if (ghResp.ok) {
|
|
147
168
|
const data = await ghResp.json();
|
|
148
|
-
const entry = data['
|
|
169
|
+
const entry = data['${endpoint}'];
|
|
149
170
|
if (entry && entry.queryId) return entry.queryId;
|
|
150
171
|
}
|
|
151
172
|
} catch {}
|
|
152
173
|
return null;
|
|
153
|
-
}`)
|
|
174
|
+
}`);
|
|
175
|
+
// Validate queryId format to prevent injection from untrusted upstream
|
|
176
|
+
const queryId = typeof resolved === 'string' && /^[A-Za-z0-9_-]+$/.test(resolved) ? resolved : fallbackQueryId;
|
|
154
177
|
// Build auth headers
|
|
155
178
|
const headers = JSON.stringify({
|
|
156
|
-
|
|
179
|
+
Authorization: `Bearer ${decodeURIComponent(BEARER_TOKEN)}`,
|
|
157
180
|
'X-Csrf-Token': ct0,
|
|
158
181
|
'X-Twitter-Auth-Type': 'OAuth2Session',
|
|
159
182
|
'X-Twitter-Active-User': 'yes',
|
|
@@ -164,10 +187,10 @@ cli({
|
|
|
164
187
|
let cursor = null;
|
|
165
188
|
for (let i = 0; i < 5 && allTweets.length < limit; i++) {
|
|
166
189
|
const fetchCount = Math.min(40, limit - allTweets.length + 5); // over-fetch slightly for promoted filtering
|
|
167
|
-
const
|
|
168
|
-
|
|
190
|
+
const variables = buildTimelineVariables(timelineType, fetchCount, cursor);
|
|
191
|
+
const apiUrl = buildHomeTimelineUrl(queryId, endpoint, variables);
|
|
169
192
|
const data = await page.evaluate(`async () => {
|
|
170
|
-
const r = await fetch("${apiUrl}", { headers: ${headers}, credentials: 'include' });
|
|
193
|
+
const r = await fetch("${apiUrl}", { method: "${method}", headers: ${headers}, credentials: 'include' });
|
|
171
194
|
return r.ok ? await r.json() : { error: r.status };
|
|
172
195
|
}`);
|
|
173
196
|
if (data?.error) {
|
|
@@ -184,3 +207,8 @@ cli({
|
|
|
184
207
|
return allTweets.slice(0, limit);
|
|
185
208
|
},
|
|
186
209
|
});
|
|
210
|
+
export const __test__ = {
|
|
211
|
+
buildTimelineVariables,
|
|
212
|
+
buildHomeTimelineUrl,
|
|
213
|
+
parseHomeTimeline,
|
|
214
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { __test__ } from './timeline.js';
|
|
3
|
+
describe('twitter timeline helpers', () => {
|
|
4
|
+
it('builds for-you variables with withCommunity', () => {
|
|
5
|
+
expect(__test__.buildTimelineVariables('for-you', 20)).toEqual({
|
|
6
|
+
count: 20,
|
|
7
|
+
includePromotedContent: false,
|
|
8
|
+
latestControlAvailable: true,
|
|
9
|
+
requestContext: 'launch',
|
|
10
|
+
withCommunity: true,
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
it('builds following variables with seenTweetIds instead of withCommunity', () => {
|
|
14
|
+
expect(__test__.buildTimelineVariables('following', 20, 'cursor-1')).toEqual({
|
|
15
|
+
count: 20,
|
|
16
|
+
includePromotedContent: false,
|
|
17
|
+
latestControlAvailable: true,
|
|
18
|
+
requestContext: 'launch',
|
|
19
|
+
seenTweetIds: [],
|
|
20
|
+
cursor: 'cursor-1',
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it('encodes variables into timeline url', () => {
|
|
24
|
+
const url = __test__.buildHomeTimelineUrl('query123', 'HomeLatestTimeline', {
|
|
25
|
+
count: 20,
|
|
26
|
+
seenTweetIds: [],
|
|
27
|
+
});
|
|
28
|
+
expect(url).toContain('/i/api/graphql/query123/HomeLatestTimeline');
|
|
29
|
+
expect(url).toContain('variables=');
|
|
30
|
+
expect(url).toContain('features=');
|
|
31
|
+
expect(decodeURIComponent(url)).toContain('"seenTweetIds":[]');
|
|
32
|
+
});
|
|
33
|
+
it('parses tweets and bottom cursor from home timeline payload', () => {
|
|
34
|
+
const payload = {
|
|
35
|
+
data: {
|
|
36
|
+
home: {
|
|
37
|
+
home_timeline_urt: {
|
|
38
|
+
instructions: [
|
|
39
|
+
{
|
|
40
|
+
entries: [
|
|
41
|
+
{
|
|
42
|
+
entryId: 'tweet-1',
|
|
43
|
+
content: {
|
|
44
|
+
itemContent: {
|
|
45
|
+
tweet_results: {
|
|
46
|
+
result: {
|
|
47
|
+
rest_id: '1',
|
|
48
|
+
legacy: {
|
|
49
|
+
full_text: 'hello',
|
|
50
|
+
favorite_count: 3,
|
|
51
|
+
retweet_count: 2,
|
|
52
|
+
reply_count: 1,
|
|
53
|
+
created_at: 'now',
|
|
54
|
+
},
|
|
55
|
+
core: {
|
|
56
|
+
user_results: {
|
|
57
|
+
result: {
|
|
58
|
+
legacy: {
|
|
59
|
+
screen_name: 'alice',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
views: {
|
|
65
|
+
count: '9',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
entryId: 'cursor-bottom-1',
|
|
74
|
+
content: {
|
|
75
|
+
entryType: 'TimelineTimelineCursor',
|
|
76
|
+
cursorType: 'Bottom',
|
|
77
|
+
value: 'cursor-next',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const result = __test__.parseHomeTimeline(payload, new Set());
|
|
88
|
+
expect(result.nextCursor).toBe('cursor-next');
|
|
89
|
+
expect(result.tweets).toHaveLength(1);
|
|
90
|
+
expect(result.tweets[0]).toMatchObject({
|
|
91
|
+
id: '1',
|
|
92
|
+
author: 'alice',
|
|
93
|
+
text: 'hello',
|
|
94
|
+
likes: 3,
|
|
95
|
+
retweets: 2,
|
|
96
|
+
replies: 1,
|
|
97
|
+
views: 9,
|
|
98
|
+
created_at: 'now',
|
|
99
|
+
url: 'https://x.com/alice/status/1',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CliError } from '../../errors.js';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import { formatSummaryRow, wikiFetch } from './utils.js';
|
|
4
|
+
cli({
|
|
5
|
+
site: 'wikipedia',
|
|
6
|
+
name: 'random',
|
|
7
|
+
description: 'Get a random Wikipedia article',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [{ name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' }],
|
|
11
|
+
columns: ['title', 'description', 'extract', 'url'],
|
|
12
|
+
func: async (_page, args) => {
|
|
13
|
+
const lang = args.lang || 'en';
|
|
14
|
+
const data = (await wikiFetch(lang, '/api/rest_v1/page/random/summary'));
|
|
15
|
+
if (!data?.title)
|
|
16
|
+
throw new CliError('NOT_FOUND', 'No random article returned', 'Try again');
|
|
17
|
+
return [formatSummaryRow(data, lang)];
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cli, Strategy } from '../../registry.js';
|
|
2
1
|
import { CliError } from '../../errors.js';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
3
|
import { wikiFetch } from './utils.js';
|
|
4
4
|
cli({
|
|
5
5
|
site: 'wikipedia',
|
|
@@ -16,8 +16,8 @@ cli({
|
|
|
16
16
|
func: async (_page, args) => {
|
|
17
17
|
const limit = Math.max(1, Math.min(Number(args.limit), 50));
|
|
18
18
|
const lang = args.lang || 'en';
|
|
19
|
-
const q = encodeURIComponent(args.
|
|
20
|
-
const data = await wikiFetch(lang, `/w/api.php?action=query&list=search&srsearch=${q}&srlimit=${limit}&format=json&utf8=1`);
|
|
19
|
+
const q = encodeURIComponent(args.query);
|
|
20
|
+
const data = (await wikiFetch(lang, `/w/api.php?action=query&list=search&srsearch=${q}&srlimit=${limit}&format=json&utf8=1`));
|
|
21
21
|
const results = data?.query?.search;
|
|
22
22
|
if (!results?.length)
|
|
23
23
|
throw new CliError('NOT_FOUND', 'No articles found', 'Try a different keyword');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { cli, Strategy } from '../../registry.js';
|
|
2
1
|
import { CliError } from '../../errors.js';
|
|
3
|
-
import {
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import { formatSummaryRow, wikiFetch } from './utils.js';
|
|
4
4
|
cli({
|
|
5
5
|
site: 'wikipedia',
|
|
6
6
|
name: 'summary',
|
|
@@ -15,14 +15,9 @@ cli({
|
|
|
15
15
|
func: async (_page, args) => {
|
|
16
16
|
const lang = args.lang || 'en';
|
|
17
17
|
const title = encodeURIComponent(args.title.replace(/ /g, '_'));
|
|
18
|
-
const data = await wikiFetch(lang, `/api/rest_v1/page/summary/${title}`);
|
|
18
|
+
const data = (await wikiFetch(lang, `/api/rest_v1/page/summary/${title}`));
|
|
19
19
|
if (!data?.title)
|
|
20
20
|
throw new CliError('NOT_FOUND', `Article "${args.title}" not found`, 'Try searching first: opencli wikipedia search <keyword>');
|
|
21
|
-
return [
|
|
22
|
-
title: data.title,
|
|
23
|
-
description: data.description ?? '-',
|
|
24
|
-
extract: (data.extract ?? '').slice(0, 300),
|
|
25
|
-
url: data.content_urls?.desktop?.page ?? `https://${lang}.wikipedia.org/wiki/${title}`,
|
|
26
|
-
}];
|
|
21
|
+
return [formatSummaryRow(data, lang)];
|
|
27
22
|
},
|
|
28
23
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CliError } from '../../errors.js';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import { DESC_MAX_LEN, wikiFetch } from './utils.js';
|
|
4
|
+
cli({
|
|
5
|
+
site: 'wikipedia',
|
|
6
|
+
name: 'trending',
|
|
7
|
+
description: 'Most-read Wikipedia articles (yesterday)',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [
|
|
11
|
+
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
|
|
12
|
+
{ name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['rank', 'title', 'description', 'views'],
|
|
15
|
+
func: async (_page, args) => {
|
|
16
|
+
const lang = args.lang || 'en';
|
|
17
|
+
const limit = Math.max(1, Math.min(Number(args.limit), 50));
|
|
18
|
+
// Use yesterday's UTC date — Wikipedia API expects UTC and yesterday
|
|
19
|
+
// guarantees data availability (today's aggregation may be incomplete).
|
|
20
|
+
const d = new Date(Date.now() - 86_400_000);
|
|
21
|
+
const yyyy = d.getUTCFullYear();
|
|
22
|
+
const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
|
|
23
|
+
const dd = String(d.getUTCDate()).padStart(2, '0');
|
|
24
|
+
const data = (await wikiFetch(lang, `/api/rest_v1/feed/featured/${yyyy}/${mm}/${dd}`));
|
|
25
|
+
const articles = data?.mostread?.articles;
|
|
26
|
+
if (!articles?.length)
|
|
27
|
+
throw new CliError('NOT_FOUND', 'No trending articles available', 'Try a different language with --lang');
|
|
28
|
+
return articles.slice(0, limit).map((a, i) => ({
|
|
29
|
+
rank: i + 1,
|
|
30
|
+
title: a.title ?? '-',
|
|
31
|
+
description: (a.description ?? '-').slice(0, DESC_MAX_LEN),
|
|
32
|
+
views: a.views ?? 0,
|
|
33
|
+
}));
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -5,4 +5,32 @@
|
|
|
5
5
|
* REST API: https://en.wikipedia.org/api/rest_v1/
|
|
6
6
|
* Action API: https://en.wikipedia.org/w/api.php
|
|
7
7
|
*/
|
|
8
|
+
/** Maximum character length for article extract fields. */
|
|
9
|
+
export declare const EXTRACT_MAX_LEN = 300;
|
|
10
|
+
/** Maximum character length for short description fields. */
|
|
11
|
+
export declare const DESC_MAX_LEN = 80;
|
|
12
|
+
/** Response shape shared by /page/summary and /page/random/summary endpoints. */
|
|
13
|
+
export interface WikiSummary {
|
|
14
|
+
title?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
extract?: string;
|
|
17
|
+
content_urls?: {
|
|
18
|
+
desktop?: {
|
|
19
|
+
page?: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Article entry returned by the /feed/featured most-read endpoint. */
|
|
24
|
+
export interface WikiMostReadArticle {
|
|
25
|
+
title?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
views?: number;
|
|
28
|
+
}
|
|
8
29
|
export declare function wikiFetch(lang: string, path: string): Promise<unknown>;
|
|
30
|
+
/** Map a WikiSummary API response to the standard output row. */
|
|
31
|
+
export declare function formatSummaryRow(data: WikiSummary, lang: string): {
|
|
32
|
+
title: string;
|
|
33
|
+
description: string;
|
|
34
|
+
extract: string;
|
|
35
|
+
url: string;
|
|
36
|
+
};
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* Action API: https://en.wikipedia.org/w/api.php
|
|
7
7
|
*/
|
|
8
8
|
import { CliError } from '../../errors.js';
|
|
9
|
+
/** Maximum character length for article extract fields. */
|
|
10
|
+
export const EXTRACT_MAX_LEN = 300;
|
|
11
|
+
/** Maximum character length for short description fields. */
|
|
12
|
+
export const DESC_MAX_LEN = 80;
|
|
9
13
|
export async function wikiFetch(lang, path) {
|
|
10
14
|
const url = `https://${lang}.wikipedia.org${path}`;
|
|
11
15
|
const resp = await fetch(url, {
|
|
@@ -16,3 +20,12 @@ export async function wikiFetch(lang, path) {
|
|
|
16
20
|
}
|
|
17
21
|
return resp.json();
|
|
18
22
|
}
|
|
23
|
+
/** Map a WikiSummary API response to the standard output row. */
|
|
24
|
+
export function formatSummaryRow(data, lang) {
|
|
25
|
+
return {
|
|
26
|
+
title: data.title,
|
|
27
|
+
description: data.description ?? '-',
|
|
28
|
+
extract: (data.extract ?? '').slice(0, EXTRACT_MAX_LEN),
|
|
29
|
+
url: data.content_urls?.desktop?.page ?? `https://${lang}.wikipedia.org`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -326,7 +326,7 @@ cli({
|
|
|
326
326
|
strategy: Strategy.COOKIE,
|
|
327
327
|
browser: true,
|
|
328
328
|
args: [
|
|
329
|
-
{ name: 'note-id', type: 'string', required: true, help: 'Note ID (from creator-notes or note-detail page URL)' },
|
|
329
|
+
{ name: 'note-id', positional: true, type: 'string', required: true, help: 'Note ID (from creator-notes or note-detail page URL)' },
|
|
330
330
|
],
|
|
331
331
|
columns: ['section', 'metric', 'value', 'extra'],
|
|
332
332
|
func: async (page, kwargs) => {
|
|
@@ -15,6 +15,8 @@ function createPageMock(evaluateResult) {
|
|
|
15
15
|
click: vi.fn().mockResolvedValue(undefined),
|
|
16
16
|
typeText: vi.fn().mockResolvedValue(undefined),
|
|
17
17
|
pressKey: vi.fn().mockResolvedValue(undefined),
|
|
18
|
+
scrollTo: vi.fn().mockResolvedValue(undefined),
|
|
19
|
+
getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
|
|
18
20
|
wait: vi.fn().mockResolvedValue(undefined),
|
|
19
21
|
tabs: vi.fn().mockResolvedValue([]),
|
|
20
22
|
closeTab: vi.fn().mockResolvedValue(undefined),
|
|
@@ -18,6 +18,8 @@ function createPageMock(evaluateResult, interceptedRequests = []) {
|
|
|
18
18
|
click: vi.fn().mockResolvedValue(undefined),
|
|
19
19
|
typeText: vi.fn().mockResolvedValue(undefined),
|
|
20
20
|
pressKey: vi.fn().mockResolvedValue(undefined),
|
|
21
|
+
scrollTo: vi.fn().mockResolvedValue(undefined),
|
|
22
|
+
getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
|
|
21
23
|
wait: vi.fn().mockResolvedValue(undefined),
|
|
22
24
|
tabs: vi.fn().mockResolvedValue([]),
|
|
23
25
|
closeTab: vi.fn().mockResolvedValue(undefined),
|
|
@@ -16,7 +16,7 @@ cli({
|
|
|
16
16
|
domain: 'www.xiaohongshu.com',
|
|
17
17
|
strategy: Strategy.COOKIE,
|
|
18
18
|
args: [
|
|
19
|
-
{ name: 'note-id', required: true, help: 'Note ID (from URL)' },
|
|
19
|
+
{ name: 'note-id', positional: true, required: true, help: 'Note ID (from URL)' },
|
|
20
20
|
{ name: 'output', default: './xiaohongshu-downloads', help: 'Output directory' },
|
|
21
21
|
],
|
|
22
22
|
columns: ['index', 'type', 'status', 'size'],
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
site: xueqiu
|
|
2
|
+
name: earnings-date
|
|
3
|
+
description: 获取股票预计财报发布日期(公司大事)
|
|
4
|
+
domain: xueqiu.com
|
|
5
|
+
browser: true
|
|
6
|
+
|
|
7
|
+
args:
|
|
8
|
+
symbol:
|
|
9
|
+
positional: true
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
description: 股票代码,如 SH600519、SZ000858、00700
|
|
13
|
+
next:
|
|
14
|
+
type: bool
|
|
15
|
+
default: false
|
|
16
|
+
description: 仅返回最近一次未发布的财报日期
|
|
17
|
+
limit:
|
|
18
|
+
type: int
|
|
19
|
+
default: 10
|
|
20
|
+
description: 返回数量,默认 10
|
|
21
|
+
|
|
22
|
+
pipeline:
|
|
23
|
+
- navigate: https://xueqiu.com
|
|
24
|
+
- evaluate: |
|
|
25
|
+
(async () => {
|
|
26
|
+
const symbol = (${{ args.symbol | json }} || '').toUpperCase();
|
|
27
|
+
const onlyNext = ${{ args.next }};
|
|
28
|
+
if (!symbol) throw new Error('Missing argument: symbol');
|
|
29
|
+
const resp = await fetch(
|
|
30
|
+
`https://stock.xueqiu.com/v5/stock/screener/event/list.json?symbol=${encodeURIComponent(symbol)}&page=1&size=100`,
|
|
31
|
+
{ credentials: 'include' }
|
|
32
|
+
);
|
|
33
|
+
if (!resp.ok) throw new Error('HTTP ' + resp.status + ' Hint: Not logged in?');
|
|
34
|
+
const d = await resp.json();
|
|
35
|
+
if (!d.data || !d.data.items) throw new Error('获取失败: ' + JSON.stringify(d));
|
|
36
|
+
|
|
37
|
+
// subtype 2 = 预计财报发布
|
|
38
|
+
let items = d.data.items.filter(item => item.subtype === 2);
|
|
39
|
+
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
let results = items.map(item => {
|
|
42
|
+
const ts = item.timestamp;
|
|
43
|
+
const dateStr = ts ? new Date(ts).toISOString().split('T')[0] : null;
|
|
44
|
+
const isFuture = ts && ts > now;
|
|
45
|
+
return {
|
|
46
|
+
date: dateStr,
|
|
47
|
+
report: item.message,
|
|
48
|
+
status: isFuture ? '⏳ 未发布' : '✅ 已发布',
|
|
49
|
+
_ts: ts,
|
|
50
|
+
_future: isFuture
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (onlyNext) {
|
|
55
|
+
const future = results.filter(r => r._future).sort((a, b) => a._ts - b._ts);
|
|
56
|
+
results = future.length ? [future[0]] : [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return results;
|
|
60
|
+
})()
|
|
61
|
+
|
|
62
|
+
- map:
|
|
63
|
+
date: ${{ item.date }}
|
|
64
|
+
report: ${{ item.report }}
|
|
65
|
+
status: ${{ item.status }}
|
|
66
|
+
|
|
67
|
+
- limit: ${{ args.limit }}
|
|
68
|
+
|
|
69
|
+
columns: [date, report, status]
|
|
@@ -6,7 +6,9 @@ browser: true
|
|
|
6
6
|
|
|
7
7
|
args:
|
|
8
8
|
query:
|
|
9
|
+
positional: true
|
|
9
10
|
type: str
|
|
11
|
+
required: true
|
|
10
12
|
description: 搜索关键词,如 茅台、AAPL、腾讯
|
|
11
13
|
limit:
|
|
12
14
|
type: int
|
|
@@ -19,7 +21,6 @@ pipeline:
|
|
|
19
21
|
(async () => {
|
|
20
22
|
const query = ${{ args.query | json }};
|
|
21
23
|
const count = ${{ args.limit }};
|
|
22
|
-
if (!query) throw new Error('Missing argument: query');
|
|
23
24
|
const resp = await fetch(`https://xueqiu.com/stock/search.json?code=${encodeURIComponent(query)}&size=${count}`, {credentials: 'include'});
|
|
24
25
|
if (!resp.ok) throw new Error('HTTP ' + resp.status + ' Hint: Not logged in?');
|
|
25
26
|
const d = await resp.json();
|
|
@@ -10,7 +10,7 @@ cli({
|
|
|
10
10
|
domain: 'finance.yahoo.com',
|
|
11
11
|
strategy: Strategy.COOKIE,
|
|
12
12
|
args: [
|
|
13
|
-
{ name: 'symbol', required: true, help: 'Stock ticker (e.g. AAPL, MSFT, TSLA)' },
|
|
13
|
+
{ name: 'symbol', required: true, positional: true, help: 'Stock ticker (e.g. AAPL, MSFT, TSLA)' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['symbol', 'name', 'price', 'change', 'changePercent', 'open', 'high', 'low', 'volume', 'marketCap'],
|
|
16
16
|
func: async (page, kwargs) => {
|