@jackwener/opencli 1.4.0 → 1.5.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/.github/actions/setup-chrome/action.yml +5 -4
- package/.github/workflows/build-extension.yml +2 -6
- package/.github/workflows/ci.yml +37 -3
- package/.github/workflows/e2e-headed.yml +16 -3
- package/CHANGELOG.md +23 -0
- package/PRIVACY.md +57 -0
- package/README.md +36 -7
- package/README.zh-CN.md +13 -6
- package/SKILL.md +103 -2
- package/dist/browser/cdp.d.ts +2 -1
- package/dist/browser/discover.d.ts +4 -1
- package/dist/browser/discover.js +6 -2
- package/dist/browser/errors.d.ts +2 -2
- package/dist/browser/errors.js +4 -12
- package/dist/browser/mcp.d.ts +2 -1
- package/dist/build-manifest.d.ts +2 -0
- package/dist/build-manifest.js +39 -14
- package/dist/build-manifest.test.js +21 -0
- package/dist/capabilityRouting.d.ts +2 -0
- package/dist/capabilityRouting.js +2 -1
- package/dist/cli-manifest.json +1838 -151
- package/dist/cli.js +34 -3
- package/dist/clis/36kr/article.d.ts +1 -0
- package/dist/clis/36kr/article.js +62 -0
- package/dist/clis/36kr/hot.d.ts +3 -0
- package/dist/clis/36kr/hot.js +80 -0
- package/dist/clis/36kr/hot.test.d.ts +1 -0
- package/dist/clis/36kr/hot.test.js +15 -0
- package/dist/clis/36kr/news.d.ts +1 -0
- package/dist/clis/36kr/news.js +51 -0
- package/dist/clis/36kr/news.test.d.ts +1 -0
- package/dist/clis/36kr/news.test.js +85 -0
- package/dist/clis/36kr/search.d.ts +1 -0
- package/dist/clis/36kr/search.js +72 -0
- package/dist/clis/apple-podcasts/search.js +2 -1
- package/dist/clis/arxiv/search.js +2 -2
- package/dist/clis/bbc/news.js +0 -1
- package/dist/clis/bilibili/comments.d.ts +5 -0
- package/dist/clis/bilibili/comments.js +40 -0
- package/dist/clis/bilibili/comments.test.d.ts +1 -0
- package/dist/clis/bilibili/comments.test.js +82 -0
- package/dist/clis/chatgpt/ask.js +29 -14
- package/dist/clis/chatgpt/ax.d.ts +6 -0
- package/dist/clis/chatgpt/ax.js +172 -1
- package/dist/clis/chatgpt/model.d.ts +1 -0
- package/dist/clis/chatgpt/model.js +24 -0
- package/dist/clis/chatgpt/send.js +12 -3
- package/dist/clis/ctrip/search.js +0 -1
- package/dist/clis/douban/download.d.ts +1 -0
- package/dist/clis/douban/download.js +67 -0
- package/dist/clis/douban/download.test.d.ts +1 -0
- package/dist/clis/douban/download.test.js +170 -0
- package/dist/clis/douban/photos.d.ts +1 -0
- package/dist/clis/douban/photos.js +34 -0
- package/dist/clis/douban/utils.d.ts +25 -0
- package/dist/clis/douban/utils.js +190 -1
- package/dist/clis/douban/utils.test.d.ts +1 -0
- package/dist/clis/douban/utils.test.js +64 -0
- package/dist/clis/douyin/_shared/browser-fetch.d.ts +10 -0
- package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
- package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.js +5 -0
- package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
- package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
- package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
- package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
- package/dist/clis/douyin/_shared/sts2.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
- package/dist/clis/douyin/_shared/text-extra.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
- package/dist/clis/douyin/_shared/timing.d.ts +2 -0
- package/dist/clis/douyin/_shared/timing.js +22 -0
- package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/timing.test.js +28 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
- package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
- package/dist/clis/douyin/_shared/tos-upload.js +295 -0
- package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
- package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
- package/dist/clis/douyin/_shared/transcode.js +45 -0
- package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/transcode.test.js +93 -0
- package/dist/clis/douyin/_shared/types.d.ts +26 -0
- package/dist/clis/douyin/_shared/types.js +1 -0
- package/dist/clis/douyin/activities.d.ts +1 -0
- package/dist/clis/douyin/activities.js +20 -0
- package/dist/clis/douyin/activities.test.d.ts +1 -0
- package/dist/clis/douyin/activities.test.js +22 -0
- package/dist/clis/douyin/collections.d.ts +1 -0
- package/dist/clis/douyin/collections.js +22 -0
- package/dist/clis/douyin/collections.test.d.ts +1 -0
- package/dist/clis/douyin/collections.test.js +23 -0
- package/dist/clis/douyin/delete.d.ts +1 -0
- package/dist/clis/douyin/delete.js +18 -0
- package/dist/clis/douyin/delete.test.d.ts +1 -0
- package/dist/clis/douyin/delete.test.js +11 -0
- package/dist/clis/douyin/draft.d.ts +14 -0
- package/dist/clis/douyin/draft.js +237 -0
- package/dist/clis/douyin/draft.test.d.ts +1 -0
- package/dist/clis/douyin/draft.test.js +11 -0
- package/dist/clis/douyin/drafts.d.ts +1 -0
- package/dist/clis/douyin/drafts.js +23 -0
- package/dist/clis/douyin/drafts.test.d.ts +1 -0
- package/dist/clis/douyin/drafts.test.js +11 -0
- package/dist/clis/douyin/hashtag.d.ts +1 -0
- package/dist/clis/douyin/hashtag.js +45 -0
- package/dist/clis/douyin/hashtag.test.d.ts +1 -0
- package/dist/clis/douyin/hashtag.test.js +25 -0
- package/dist/clis/douyin/location.d.ts +1 -0
- package/dist/clis/douyin/location.js +24 -0
- package/dist/clis/douyin/location.test.d.ts +1 -0
- package/dist/clis/douyin/location.test.js +23 -0
- package/dist/clis/douyin/profile.d.ts +1 -0
- package/dist/clis/douyin/profile.js +28 -0
- package/dist/clis/douyin/profile.test.d.ts +1 -0
- package/dist/clis/douyin/profile.test.js +11 -0
- package/dist/clis/douyin/publish.d.ts +14 -0
- package/dist/clis/douyin/publish.js +288 -0
- package/dist/clis/douyin/publish.test.d.ts +1 -0
- package/dist/clis/douyin/publish.test.js +38 -0
- package/dist/clis/douyin/stats.d.ts +1 -0
- package/dist/clis/douyin/stats.js +27 -0
- package/dist/clis/douyin/stats.test.d.ts +1 -0
- package/dist/clis/douyin/stats.test.js +22 -0
- package/dist/clis/douyin/update.d.ts +1 -0
- package/dist/clis/douyin/update.js +31 -0
- package/dist/clis/douyin/update.test.d.ts +1 -0
- package/dist/clis/douyin/update.test.js +11 -0
- package/dist/clis/douyin/videos.d.ts +1 -0
- package/dist/clis/douyin/videos.js +34 -0
- package/dist/clis/douyin/videos.test.d.ts +1 -0
- package/dist/clis/douyin/videos.test.js +11 -0
- package/dist/clis/hackernews/search.yaml +1 -1
- package/dist/clis/imdb/person.d.ts +1 -0
- package/dist/clis/imdb/person.js +203 -0
- package/dist/clis/imdb/reviews.d.ts +1 -0
- package/dist/clis/imdb/reviews.js +88 -0
- package/dist/clis/imdb/search.d.ts +1 -0
- package/dist/clis/imdb/search.js +161 -0
- package/dist/clis/imdb/title.d.ts +1 -0
- package/dist/clis/imdb/title.js +93 -0
- package/dist/clis/imdb/top.d.ts +1 -0
- package/dist/clis/imdb/top.js +53 -0
- package/dist/clis/imdb/trending.d.ts +1 -0
- package/dist/clis/imdb/trending.js +52 -0
- package/dist/clis/imdb/utils.d.ts +46 -0
- package/dist/clis/imdb/utils.js +285 -0
- package/dist/clis/imdb/utils.test.d.ts +1 -0
- package/dist/clis/imdb/utils.test.js +88 -0
- package/dist/clis/instagram/search.yaml +2 -1
- package/dist/clis/jd/item.d.ts +4 -0
- package/dist/clis/jd/item.js +16 -15
- package/dist/clis/jd/item.test.js +16 -1
- package/dist/clis/linux-do/categories.yaml +38 -9
- package/dist/clis/linux-do/category.d.ts +1 -0
- package/dist/clis/linux-do/category.js +36 -0
- package/dist/clis/linux-do/feed.d.ts +45 -0
- package/dist/clis/linux-do/feed.js +397 -0
- package/dist/clis/linux-do/feed.test.d.ts +1 -0
- package/dist/clis/linux-do/feed.test.js +118 -0
- package/dist/clis/linux-do/hot.d.ts +1 -0
- package/dist/clis/linux-do/hot.js +25 -0
- package/dist/clis/linux-do/latest.d.ts +1 -0
- package/dist/clis/linux-do/latest.js +18 -0
- package/dist/clis/linux-do/search.yaml +3 -1
- package/dist/clis/linux-do/tags.yaml +41 -0
- package/dist/clis/linux-do/topic.yaml +41 -3
- package/dist/clis/linux-do/user-posts.yaml +67 -0
- package/dist/clis/linux-do/user-topics.yaml +54 -0
- package/dist/clis/medium/search.js +1 -1
- package/dist/clis/paperreview/commands.test.d.ts +3 -0
- package/dist/clis/paperreview/commands.test.js +243 -0
- package/dist/clis/paperreview/feedback.d.ts +1 -0
- package/dist/clis/paperreview/feedback.js +52 -0
- package/dist/clis/paperreview/review.d.ts +1 -0
- package/dist/clis/paperreview/review.js +37 -0
- package/dist/clis/paperreview/submit.d.ts +1 -0
- package/dist/clis/paperreview/submit.js +85 -0
- package/dist/clis/paperreview/utils.d.ts +46 -0
- package/dist/clis/paperreview/utils.js +197 -0
- package/dist/clis/paperreview/utils.test.d.ts +1 -0
- package/dist/clis/paperreview/utils.test.js +49 -0
- package/dist/clis/producthunt/browse.d.ts +1 -0
- package/dist/clis/producthunt/browse.js +99 -0
- package/dist/clis/producthunt/hot.d.ts +1 -0
- package/dist/clis/producthunt/hot.js +110 -0
- package/dist/clis/producthunt/posts.d.ts +1 -0
- package/dist/clis/producthunt/posts.js +28 -0
- package/dist/clis/producthunt/today.d.ts +1 -0
- package/dist/clis/producthunt/today.js +35 -0
- package/dist/clis/producthunt/utils.d.ts +29 -0
- package/dist/clis/producthunt/utils.js +99 -0
- package/dist/clis/producthunt/utils.test.d.ts +1 -0
- package/dist/clis/producthunt/utils.test.js +64 -0
- package/dist/clis/reuters/search.js +0 -1
- package/dist/clis/twitter/article.js +4 -28
- package/dist/clis/twitter/likes.d.ts +24 -0
- package/dist/clis/twitter/likes.js +217 -0
- package/dist/clis/twitter/likes.test.d.ts +1 -0
- package/dist/clis/twitter/likes.test.js +85 -0
- package/dist/clis/twitter/profile.js +4 -28
- package/dist/clis/twitter/search.js +7 -4
- package/dist/clis/twitter/search.test.js +56 -2
- package/dist/clis/twitter/shared.d.ts +6 -0
- package/dist/clis/twitter/shared.js +35 -0
- package/dist/clis/twitter/timeline.js +2 -13
- package/dist/clis/weibo/comments.d.ts +1 -0
- package/dist/clis/weibo/comments.js +53 -0
- package/dist/clis/weibo/feed.d.ts +1 -0
- package/dist/clis/weibo/feed.js +56 -0
- package/dist/clis/weibo/hot.js +0 -1
- package/dist/clis/weibo/me.d.ts +1 -0
- package/dist/clis/weibo/me.js +76 -0
- package/dist/clis/weibo/post.d.ts +1 -0
- package/dist/clis/weibo/post.js +75 -0
- package/dist/clis/weibo/user.d.ts +1 -0
- package/dist/clis/weibo/user.js +63 -0
- package/dist/clis/weibo/utils.d.ts +6 -0
- package/dist/clis/weibo/utils.js +30 -0
- package/dist/clis/weixin/download.d.ts +17 -0
- package/dist/clis/weixin/download.js +88 -20
- package/dist/clis/weread/book.js +2 -2
- package/dist/clis/weread/commands.test.d.ts +3 -0
- package/dist/clis/weread/commands.test.js +43 -0
- package/dist/clis/weread/highlights.js +2 -2
- package/dist/clis/weread/notebooks.js +2 -2
- package/dist/clis/weread/notes.js +3 -3
- package/dist/clis/weread/search.js +3 -2
- package/dist/clis/weread/shelf.js +2 -2
- package/dist/clis/weread/utils.d.ts +4 -4
- package/dist/clis/weread/utils.js +32 -14
- package/dist/clis/weread/utils.test.js +1 -28
- package/dist/clis/xiaohongshu/comments.d.ts +5 -0
- package/dist/clis/xiaohongshu/comments.js +74 -0
- package/dist/clis/xiaohongshu/comments.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/comments.test.js +79 -0
- package/dist/clis/xiaohongshu/publish.js +114 -18
- package/dist/clis/xiaohongshu/publish.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/publish.test.js +119 -0
- package/dist/clis/xueqiu/search.yaml +2 -1
- package/dist/clis/yahoo-finance/quote.js +0 -1
- package/dist/clis/youtube/channel.d.ts +1 -0
- package/dist/clis/youtube/channel.js +150 -0
- package/dist/clis/youtube/comments.d.ts +1 -0
- package/dist/clis/youtube/comments.js +95 -0
- package/dist/clis/youtube/search.js +0 -1
- package/dist/clis/zhihu/search.yaml +2 -1
- package/dist/commanderAdapter.d.ts +1 -0
- package/dist/commanderAdapter.js +176 -29
- package/dist/commanderAdapter.test.d.ts +1 -0
- package/dist/commanderAdapter.test.js +62 -0
- package/dist/daemon.js +17 -1
- package/dist/discovery.js +8 -14
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +9 -2
- package/dist/download/index.js +63 -51
- package/dist/download/index.test.js +17 -4
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js +15 -32
- package/dist/execution.d.ts +1 -3
- package/dist/execution.js +21 -1
- package/dist/external-clis.yaml +0 -17
- package/dist/hooks.js +2 -0
- package/dist/main.js +5 -0
- package/dist/output.js +5 -1
- package/dist/pipeline/executor.js +3 -4
- package/dist/plugin-manifest.d.ts +70 -0
- package/dist/plugin-manifest.js +160 -0
- package/dist/plugin-manifest.test.d.ts +4 -0
- package/dist/plugin-manifest.test.js +179 -0
- package/dist/plugin.d.ts +38 -5
- package/dist/plugin.js +267 -33
- package/dist/plugin.test.js +220 -3
- package/dist/registry.d.ts +4 -0
- package/dist/registry.js +2 -0
- package/dist/runtime-detect.d.ts +21 -0
- package/dist/runtime-detect.js +32 -0
- package/dist/runtime-detect.test.d.ts +1 -0
- package/dist/runtime-detect.test.js +27 -0
- package/dist/runtime.js +1 -1
- package/dist/serialization.d.ts +2 -0
- package/dist/serialization.js +6 -0
- package/dist/types.d.ts +1 -0
- package/dist/update-check.d.ts +22 -0
- package/dist/update-check.js +112 -0
- package/dist/weixin-download.test.d.ts +1 -0
- package/dist/weixin-download.test.js +30 -0
- package/dist/weread-private-api-regression.test.d.ts +1 -0
- package/dist/weread-private-api-regression.test.js +122 -0
- package/dist/weread-search-regression.test.d.ts +1 -0
- package/dist/weread-search-regression.test.js +39 -0
- package/dist/yaml-schema.d.ts +3 -0
- package/dist/yaml-schema.js +18 -1
- package/docs/.vitepress/config.mts +17 -0
- package/docs/adapters/browser/36kr.md +47 -0
- package/docs/adapters/browser/douban.md +14 -0
- package/docs/adapters/browser/douyin.md +75 -0
- package/docs/adapters/browser/imdb.md +47 -0
- package/docs/adapters/browser/jd.md +2 -2
- package/docs/adapters/browser/linux-do.md +181 -20
- package/docs/adapters/browser/paperreview.md +43 -0
- package/docs/adapters/browser/producthunt.md +49 -0
- package/docs/adapters/browser/twitter.md +6 -0
- package/docs/adapters/desktop/chatgpt.md +5 -0
- package/docs/adapters/index.md +12 -3
- package/docs/advanced/download.md +4 -0
- package/docs/advanced/rate-limiter-plugin.md +99 -0
- package/docs/guide/electron-app-cli.md +200 -0
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +87 -0
- package/docs/zh/guide/electron-app-cli.md +188 -0
- package/docs/zh/guide/getting-started.md +1 -0
- package/docs/zh/guide/plugins.md +65 -0
- package/extension/dist/background.js +508 -518
- package/extension/manifest.json +6 -2
- package/extension/package.json +2 -1
- package/extension/popup.html +84 -0
- package/extension/popup.js +25 -0
- package/extension/scripts/package-release.mjs +179 -0
- package/extension/src/background.ts +22 -1
- package/package.json +4 -1
- package/scripts/postinstall.js +10 -0
- package/src/browser/cdp.ts +2 -1
- package/src/browser/discover.ts +8 -3
- package/src/browser/errors.ts +13 -14
- package/src/browser/mcp.ts +2 -1
- package/src/build-manifest.test.ts +23 -0
- package/src/build-manifest.ts +40 -15
- package/src/capabilityRouting.ts +2 -1
- package/src/cli.ts +35 -3
- package/src/clis/36kr/article.ts +69 -0
- package/src/clis/36kr/hot.test.ts +19 -0
- package/src/clis/36kr/hot.ts +100 -0
- package/src/clis/36kr/news.test.ts +90 -0
- package/src/clis/36kr/news.ts +54 -0
- package/src/clis/36kr/search.ts +78 -0
- package/src/clis/apple-podcasts/search.ts +2 -1
- package/src/clis/arxiv/search.ts +2 -2
- package/src/clis/bbc/news.ts +0 -1
- package/src/clis/bilibili/comments.test.ts +102 -0
- package/src/clis/bilibili/comments.ts +44 -0
- package/src/clis/chatgpt/ask.ts +28 -14
- package/src/clis/chatgpt/ax.ts +180 -1
- package/src/clis/chatgpt/model.ts +27 -0
- package/src/clis/chatgpt/send.ts +16 -6
- package/src/clis/ctrip/search.ts +0 -1
- package/src/clis/douban/download.test.ts +196 -0
- package/src/clis/douban/download.ts +78 -0
- package/src/clis/douban/photos.ts +36 -0
- package/src/clis/douban/utils.test.ts +97 -0
- package/src/clis/douban/utils.ts +232 -1
- package/src/clis/douyin/_shared/browser-fetch.test.ts +38 -0
- package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
- package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
- package/src/clis/douyin/_shared/creation-id.ts +8 -0
- package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
- package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
- package/src/clis/douyin/_shared/sts2.ts +20 -0
- package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
- package/src/clis/douyin/_shared/text-extra.ts +33 -0
- package/src/clis/douyin/_shared/timing.test.ts +38 -0
- package/src/clis/douyin/_shared/timing.ts +22 -0
- package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
- package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
- package/src/clis/douyin/_shared/tos-upload.ts +444 -0
- package/src/clis/douyin/_shared/transcode.test.ts +117 -0
- package/src/clis/douyin/_shared/transcode.ts +78 -0
- package/src/clis/douyin/_shared/types.ts +29 -0
- package/src/clis/douyin/activities.test.ts +25 -0
- package/src/clis/douyin/activities.ts +23 -0
- package/src/clis/douyin/collections.test.ts +26 -0
- package/src/clis/douyin/collections.ts +25 -0
- package/src/clis/douyin/delete.test.ts +12 -0
- package/src/clis/douyin/delete.ts +20 -0
- package/src/clis/douyin/draft.test.ts +12 -0
- package/src/clis/douyin/draft.ts +282 -0
- package/src/clis/douyin/drafts.test.ts +12 -0
- package/src/clis/douyin/drafts.ts +27 -0
- package/src/clis/douyin/hashtag.test.ts +28 -0
- package/src/clis/douyin/hashtag.ts +56 -0
- package/src/clis/douyin/location.test.ts +26 -0
- package/src/clis/douyin/location.ts +27 -0
- package/src/clis/douyin/profile.test.ts +12 -0
- package/src/clis/douyin/profile.ts +37 -0
- package/src/clis/douyin/publish.test.ts +45 -0
- package/src/clis/douyin/publish.ts +340 -0
- package/src/clis/douyin/stats.test.ts +25 -0
- package/src/clis/douyin/stats.ts +30 -0
- package/src/clis/douyin/update.test.ts +12 -0
- package/src/clis/douyin/update.ts +43 -0
- package/src/clis/douyin/videos.test.ts +12 -0
- package/src/clis/douyin/videos.ts +49 -0
- package/src/clis/hackernews/search.yaml +1 -1
- package/src/clis/imdb/person.ts +232 -0
- package/src/clis/imdb/reviews.ts +111 -0
- package/src/clis/imdb/search.ts +179 -0
- package/src/clis/imdb/title.ts +121 -0
- package/src/clis/imdb/top.ts +67 -0
- package/src/clis/imdb/trending.ts +66 -0
- package/src/clis/imdb/utils.test.ts +117 -0
- package/src/clis/imdb/utils.ts +305 -0
- package/src/clis/instagram/search.yaml +2 -1
- package/src/clis/jd/item.test.ts +18 -1
- package/src/clis/jd/item.ts +18 -15
- package/src/clis/linux-do/categories.yaml +38 -9
- package/src/clis/linux-do/category.ts +37 -0
- package/src/clis/linux-do/feed.test.ts +132 -0
- package/src/clis/linux-do/feed.ts +501 -0
- package/src/clis/linux-do/hot.ts +26 -0
- package/src/clis/linux-do/latest.ts +19 -0
- package/src/clis/linux-do/search.yaml +3 -1
- package/src/clis/linux-do/tags.yaml +41 -0
- package/src/clis/linux-do/topic.yaml +41 -3
- package/src/clis/linux-do/user-posts.yaml +67 -0
- package/src/clis/linux-do/user-topics.yaml +54 -0
- package/src/clis/medium/search.ts +1 -1
- package/src/clis/paperreview/commands.test.ts +283 -0
- package/src/clis/paperreview/feedback.ts +64 -0
- package/src/clis/paperreview/review.ts +47 -0
- package/src/clis/paperreview/submit.ts +119 -0
- package/src/clis/paperreview/utils.test.ts +68 -0
- package/src/clis/paperreview/utils.ts +276 -0
- package/src/clis/producthunt/browse.ts +109 -0
- package/src/clis/producthunt/hot.ts +127 -0
- package/src/clis/producthunt/posts.ts +29 -0
- package/src/clis/producthunt/today.ts +37 -0
- package/src/clis/producthunt/utils.test.ts +72 -0
- package/src/clis/producthunt/utils.ts +122 -0
- package/src/clis/reuters/search.ts +0 -1
- package/src/clis/twitter/article.ts +5 -28
- package/src/clis/twitter/likes.test.ts +91 -0
- package/src/clis/twitter/likes.ts +256 -0
- package/src/clis/twitter/profile.ts +5 -28
- package/src/clis/twitter/search.test.ts +71 -2
- package/src/clis/twitter/search.ts +8 -4
- package/src/clis/twitter/shared.ts +45 -0
- package/src/clis/twitter/timeline.ts +2 -13
- package/src/clis/weibo/comments.ts +54 -0
- package/src/clis/weibo/feed.ts +57 -0
- package/src/clis/weibo/hot.ts +0 -1
- package/src/clis/weibo/me.ts +77 -0
- package/src/clis/weibo/post.ts +77 -0
- package/src/clis/weibo/user.ts +64 -0
- package/src/clis/weibo/utils.ts +32 -0
- package/src/clis/weixin/download.ts +114 -20
- package/src/clis/weread/book.ts +2 -2
- package/src/clis/weread/commands.test.ts +57 -0
- package/src/clis/weread/highlights.ts +2 -2
- package/src/clis/weread/notebooks.ts +2 -2
- package/src/clis/weread/notes.ts +3 -3
- package/src/clis/weread/search.ts +3 -2
- package/src/clis/weread/shelf.ts +2 -2
- package/src/clis/weread/utils.test.ts +1 -32
- package/src/clis/weread/utils.ts +41 -16
- package/src/clis/xiaohongshu/comments.test.ts +96 -0
- package/src/clis/xiaohongshu/comments.ts +81 -0
- package/src/clis/xiaohongshu/publish.test.ts +137 -0
- package/src/clis/xiaohongshu/publish.ts +129 -18
- package/src/clis/xueqiu/search.yaml +2 -1
- package/src/clis/yahoo-finance/quote.ts +0 -1
- package/src/clis/youtube/channel.ts +155 -0
- package/src/clis/youtube/comments.ts +97 -0
- package/src/clis/youtube/search.ts +0 -1
- package/src/clis/zhihu/search.yaml +2 -1
- package/src/commanderAdapter.test.ts +78 -0
- package/src/commanderAdapter.ts +188 -24
- package/src/daemon.ts +19 -1
- package/src/discovery.ts +8 -15
- package/src/doctor.ts +13 -2
- package/src/download/index.test.ts +14 -4
- package/src/download/index.ts +67 -55
- package/src/errors.ts +25 -66
- package/src/execution.ts +28 -3
- package/src/external-clis.yaml +0 -17
- package/src/hooks.ts +1 -0
- package/src/main.ts +6 -0
- package/src/output.ts +3 -1
- package/src/pipeline/executor.ts +4 -6
- package/src/plugin-manifest.test.ts +223 -0
- package/src/plugin-manifest.ts +206 -0
- package/src/plugin.test.ts +246 -2
- package/src/plugin.ts +338 -36
- package/src/registry.ts +6 -1
- package/src/runtime-detect.test.ts +30 -0
- package/src/runtime-detect.ts +36 -0
- package/src/runtime.ts +1 -1
- package/src/serialization.ts +4 -0
- package/src/types.ts +1 -0
- package/src/update-check.ts +114 -0
- package/src/weixin-download.test.ts +64 -0
- package/src/weread-private-api-regression.test.ts +150 -0
- package/src/weread-search-regression.test.ts +44 -0
- package/src/yaml-schema.ts +20 -0
- package/tests/e2e/browser-auth.test.ts +13 -9
- package/tests/e2e/browser-public-extended.test.ts +162 -0
- package/tests/e2e/browser-public.test.ts +55 -136
- package/tests/e2e/helpers.ts +2 -1
- package/tests/e2e/public-commands.test.ts +37 -3
- package/tests/smoke/api-health.test.ts +1 -1
- package/vitest.config.ts +34 -17
- package/dist/clis/linux-do/category.yaml +0 -51
- package/dist/clis/linux-do/hot.yaml +0 -50
- package/dist/clis/linux-do/latest.yaml +0 -40
- package/src/clis/linux-do/category.yaml +0 -51
- package/src/clis/linux-do/hot.yaml +0 -50
- package/src/clis/linux-do/latest.yaml +0 -40
|
@@ -36,6 +36,7 @@ describe('twitter search command', () => {
|
|
|
36
36
|
legacy: {
|
|
37
37
|
full_text: 'hello world',
|
|
38
38
|
favorite_count: 7,
|
|
39
|
+
created_at: 'Thu Mar 26 10:30:00 +0000 2026',
|
|
39
40
|
},
|
|
40
41
|
core: {
|
|
41
42
|
user_results: {
|
|
@@ -64,12 +65,13 @@ describe('twitter search command', () => {
|
|
|
64
65
|
},
|
|
65
66
|
]),
|
|
66
67
|
};
|
|
67
|
-
const result = await command.func(page, { query: 'from:alice', limit: 5 });
|
|
68
|
+
const result = await command.func(page, { query: 'from:alice', filter: 'top', limit: 5 });
|
|
68
69
|
expect(result).toEqual([
|
|
69
70
|
{
|
|
70
71
|
id: '1',
|
|
71
72
|
author: 'alice',
|
|
72
73
|
text: 'hello world',
|
|
74
|
+
created_at: 'Thu Mar 26 10:30:00 +0000 2026',
|
|
73
75
|
likes: 7,
|
|
74
76
|
views: '12',
|
|
75
77
|
url: 'https://x.com/i/status/1',
|
|
@@ -78,6 +80,58 @@ describe('twitter search command', () => {
|
|
|
78
80
|
expect(page.installInterceptor).toHaveBeenCalledWith('SearchTimeline');
|
|
79
81
|
expect(evaluate).toHaveBeenCalledTimes(4);
|
|
80
82
|
});
|
|
83
|
+
it('uses f=live in search URL when filter is live', async () => {
|
|
84
|
+
const command = getRegistry().get('twitter/search');
|
|
85
|
+
const evaluate = vi.fn()
|
|
86
|
+
.mockResolvedValueOnce(undefined)
|
|
87
|
+
.mockResolvedValueOnce('/search');
|
|
88
|
+
const page = {
|
|
89
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
90
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
91
|
+
installInterceptor: vi.fn().mockResolvedValue(undefined),
|
|
92
|
+
evaluate,
|
|
93
|
+
autoScroll: vi.fn().mockResolvedValue(undefined),
|
|
94
|
+
getInterceptedRequests: vi.fn().mockResolvedValue([]),
|
|
95
|
+
};
|
|
96
|
+
await command.func(page, { query: 'breaking news', filter: 'live', limit: 5 });
|
|
97
|
+
const pushStateCall = evaluate.mock.calls[0][0];
|
|
98
|
+
expect(pushStateCall).toContain('f=live');
|
|
99
|
+
expect(pushStateCall).toContain(encodeURIComponent('breaking news'));
|
|
100
|
+
});
|
|
101
|
+
it('uses f=top in search URL when filter is top', async () => {
|
|
102
|
+
const command = getRegistry().get('twitter/search');
|
|
103
|
+
const evaluate = vi.fn()
|
|
104
|
+
.mockResolvedValueOnce(undefined)
|
|
105
|
+
.mockResolvedValueOnce('/search');
|
|
106
|
+
const page = {
|
|
107
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
108
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
109
|
+
installInterceptor: vi.fn().mockResolvedValue(undefined),
|
|
110
|
+
evaluate,
|
|
111
|
+
autoScroll: vi.fn().mockResolvedValue(undefined),
|
|
112
|
+
getInterceptedRequests: vi.fn().mockResolvedValue([]),
|
|
113
|
+
};
|
|
114
|
+
await command.func(page, { query: 'test', filter: 'top', limit: 5 });
|
|
115
|
+
const pushStateCall = evaluate.mock.calls[0][0];
|
|
116
|
+
expect(pushStateCall).toContain('f=top');
|
|
117
|
+
});
|
|
118
|
+
it('falls back to top when filter is omitted', async () => {
|
|
119
|
+
const command = getRegistry().get('twitter/search');
|
|
120
|
+
const evaluate = vi.fn()
|
|
121
|
+
.mockResolvedValueOnce(undefined)
|
|
122
|
+
.mockResolvedValueOnce('/search');
|
|
123
|
+
const page = {
|
|
124
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
125
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
126
|
+
installInterceptor: vi.fn().mockResolvedValue(undefined),
|
|
127
|
+
evaluate,
|
|
128
|
+
autoScroll: vi.fn().mockResolvedValue(undefined),
|
|
129
|
+
getInterceptedRequests: vi.fn().mockResolvedValue([]),
|
|
130
|
+
};
|
|
131
|
+
await command.func(page, { query: 'test', limit: 5 });
|
|
132
|
+
const pushStateCall = evaluate.mock.calls[0][0];
|
|
133
|
+
expect(pushStateCall).toContain('f=top');
|
|
134
|
+
});
|
|
81
135
|
it('throws with the final path after both attempts fail', async () => {
|
|
82
136
|
const command = getRegistry().get('twitter/search');
|
|
83
137
|
expect(command?.func).toBeTypeOf('function');
|
|
@@ -94,7 +148,7 @@ describe('twitter search command', () => {
|
|
|
94
148
|
autoScroll: vi.fn().mockResolvedValue(undefined),
|
|
95
149
|
getInterceptedRequests: vi.fn(),
|
|
96
150
|
};
|
|
97
|
-
await expect(command.func(page, { query: 'from:alice', limit: 5 }))
|
|
151
|
+
await expect(command.func(page, { query: 'from:alice', filter: 'top', limit: 5 }))
|
|
98
152
|
.rejects
|
|
99
153
|
.toThrow('Final path: /login');
|
|
100
154
|
expect(page.autoScroll).not.toHaveBeenCalled();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IPage } from '../../types.js';
|
|
2
|
+
export declare function sanitizeQueryId(resolved: unknown, fallbackId: string): string;
|
|
3
|
+
export declare function resolveTwitterQueryId(page: Pick<IPage, 'evaluate'>, operationName: string, fallbackId: string): Promise<string>;
|
|
4
|
+
export declare const __test__: {
|
|
5
|
+
sanitizeQueryId: typeof sanitizeQueryId;
|
|
6
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const QUERY_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
2
|
+
export function sanitizeQueryId(resolved, fallbackId) {
|
|
3
|
+
return typeof resolved === 'string' && QUERY_ID_PATTERN.test(resolved) ? resolved : fallbackId;
|
|
4
|
+
}
|
|
5
|
+
export async function resolveTwitterQueryId(page, operationName, fallbackId) {
|
|
6
|
+
const resolved = await page.evaluate(`async () => {
|
|
7
|
+
const operationName = ${JSON.stringify(operationName)};
|
|
8
|
+
try {
|
|
9
|
+
const ghResp = await fetch('https://raw.githubusercontent.com/fa0311/twitter-openapi/refs/heads/main/src/config/placeholder.json');
|
|
10
|
+
if (ghResp.ok) {
|
|
11
|
+
const data = await ghResp.json();
|
|
12
|
+
const entry = data?.[operationName];
|
|
13
|
+
if (entry && entry.queryId) return entry.queryId;
|
|
14
|
+
}
|
|
15
|
+
} catch {}
|
|
16
|
+
try {
|
|
17
|
+
const scripts = performance.getEntriesByType('resource')
|
|
18
|
+
.filter(r => r.name.includes('client-web') && r.name.endsWith('.js'))
|
|
19
|
+
.map(r => r.name);
|
|
20
|
+
for (const scriptUrl of scripts.slice(0, 15)) {
|
|
21
|
+
try {
|
|
22
|
+
const text = await (await fetch(scriptUrl)).text();
|
|
23
|
+
const re = new RegExp('queryId:"([A-Za-z0-9_-]+)"[^}]{0,200}operationName:"' + operationName + '"');
|
|
24
|
+
const match = text.match(re);
|
|
25
|
+
if (match) return match[1];
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
28
|
+
} catch {}
|
|
29
|
+
return null;
|
|
30
|
+
}`);
|
|
31
|
+
return sanitizeQueryId(resolved, fallbackId);
|
|
32
|
+
}
|
|
33
|
+
export const __test__ = {
|
|
34
|
+
sanitizeQueryId,
|
|
35
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
|
|
2
2
|
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import { resolveTwitterQueryId } from './shared.js';
|
|
3
4
|
// ── Twitter GraphQL constants ──────────────────────────────────────────
|
|
4
5
|
const BEARER_TOKEN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
|
|
5
6
|
const HOME_TIMELINE_QUERY_ID = 'c-CzHF1LboFilMpsx4ZCrQ';
|
|
@@ -162,19 +163,7 @@ cli({
|
|
|
162
163
|
if (!ct0)
|
|
163
164
|
throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
|
|
164
165
|
// Dynamically resolve queryId for the selected endpoint
|
|
165
|
-
const
|
|
166
|
-
try {
|
|
167
|
-
const ghResp = await fetch('https://raw.githubusercontent.com/fa0311/twitter-openapi/refs/heads/main/src/config/placeholder.json');
|
|
168
|
-
if (ghResp.ok) {
|
|
169
|
-
const data = await ghResp.json();
|
|
170
|
-
const entry = data['${endpoint}'];
|
|
171
|
-
if (entry && entry.queryId) return entry.queryId;
|
|
172
|
-
}
|
|
173
|
-
} catch {}
|
|
174
|
-
return null;
|
|
175
|
-
}`);
|
|
176
|
-
// Validate queryId format to prevent injection from untrusted upstream
|
|
177
|
-
const queryId = typeof resolved === 'string' && /^[A-Za-z0-9_-]+$/.test(resolved) ? resolved : fallbackQueryId;
|
|
166
|
+
const queryId = await resolveTwitterQueryId(page, endpoint, fallbackQueryId);
|
|
178
167
|
// Build auth headers
|
|
179
168
|
const headers = JSON.stringify({
|
|
180
169
|
Authorization: `Bearer ${decodeURIComponent(BEARER_TOKEN)}`,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weibo comments — get comments on a post.
|
|
3
|
+
*/
|
|
4
|
+
import { cli, Strategy } from '../../registry.js';
|
|
5
|
+
cli({
|
|
6
|
+
site: 'weibo',
|
|
7
|
+
name: 'comments',
|
|
8
|
+
description: 'Get comments on a Weibo post',
|
|
9
|
+
domain: 'weibo.com',
|
|
10
|
+
strategy: Strategy.COOKIE,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'id', required: true, positional: true, help: 'Post ID (numeric idstr)' },
|
|
13
|
+
{ name: 'limit', type: 'int', default: 20, help: 'Number of comments (max 50)' },
|
|
14
|
+
],
|
|
15
|
+
columns: ['rank', 'author', 'text', 'likes', 'replies', 'time'],
|
|
16
|
+
func: async (page, kwargs) => {
|
|
17
|
+
const count = Math.min(kwargs.limit || 20, 50);
|
|
18
|
+
await page.goto('https://weibo.com');
|
|
19
|
+
await page.wait(2);
|
|
20
|
+
const id = String(kwargs.id);
|
|
21
|
+
const data = await page.evaluate(`
|
|
22
|
+
(async () => {
|
|
23
|
+
const id = ${JSON.stringify(id)};
|
|
24
|
+
const count = ${count};
|
|
25
|
+
const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').trim();
|
|
26
|
+
|
|
27
|
+
const url = '/ajax/statuses/buildComments?flow=0&is_reload=1&id=' + id + '&is_show_bulletin=2&is_mix=0&count=' + count;
|
|
28
|
+
const resp = await fetch(url, {credentials: 'include'});
|
|
29
|
+
if (!resp.ok) return {error: 'HTTP ' + resp.status};
|
|
30
|
+
const data = await resp.json();
|
|
31
|
+
if (!data.ok) return {error: 'API error: ' + (data.msg || 'unknown')};
|
|
32
|
+
|
|
33
|
+
return (data.data || []).map((c, i) => {
|
|
34
|
+
const item = {
|
|
35
|
+
rank: i + 1,
|
|
36
|
+
author: c.user?.screen_name || '',
|
|
37
|
+
text: strip(c.text || ''),
|
|
38
|
+
likes: c.like_count || 0,
|
|
39
|
+
replies: c.total_number || 0,
|
|
40
|
+
time: c.created_at || '',
|
|
41
|
+
};
|
|
42
|
+
if (c.reply_comment) {
|
|
43
|
+
item.reply_to = (c.reply_comment.user?.screen_name || '') + ': ' + strip(c.reply_comment.text || '').substring(0, 80);
|
|
44
|
+
}
|
|
45
|
+
return item;
|
|
46
|
+
});
|
|
47
|
+
})()
|
|
48
|
+
`);
|
|
49
|
+
if (!Array.isArray(data))
|
|
50
|
+
return [];
|
|
51
|
+
return data;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weibo feed — home timeline from followed users.
|
|
3
|
+
*/
|
|
4
|
+
import { cli, Strategy } from '../../registry.js';
|
|
5
|
+
import { getSelfUid } from './utils.js';
|
|
6
|
+
cli({
|
|
7
|
+
site: 'weibo',
|
|
8
|
+
name: 'feed',
|
|
9
|
+
description: 'Weibo home timeline (posts from followed users)',
|
|
10
|
+
domain: 'weibo.com',
|
|
11
|
+
strategy: Strategy.COOKIE,
|
|
12
|
+
args: [
|
|
13
|
+
{ name: 'limit', type: 'int', default: 15, help: 'Number of posts (max 50)' },
|
|
14
|
+
],
|
|
15
|
+
columns: ['author', 'text', 'reposts', 'comments', 'likes', 'time', 'url'],
|
|
16
|
+
func: async (page, kwargs) => {
|
|
17
|
+
const count = Math.min(kwargs.limit || 15, 50);
|
|
18
|
+
await page.goto('https://weibo.com');
|
|
19
|
+
await page.wait(2);
|
|
20
|
+
const uid = await getSelfUid(page);
|
|
21
|
+
const data = await page.evaluate(`
|
|
22
|
+
(async () => {
|
|
23
|
+
const uid = ${JSON.stringify(uid)};
|
|
24
|
+
const count = ${count};
|
|
25
|
+
const listId = '10001' + uid;
|
|
26
|
+
const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').trim();
|
|
27
|
+
|
|
28
|
+
const resp = await fetch('/ajax/feed/unreadfriendstimeline?list_id=' + listId + '&refresh=4&since_id=0&count=' + count, {credentials: 'include'});
|
|
29
|
+
if (!resp.ok) return {error: 'HTTP ' + resp.status};
|
|
30
|
+
const data = await resp.json();
|
|
31
|
+
if (!data.ok) return {error: 'API error: ' + (data.msg || 'unknown')};
|
|
32
|
+
|
|
33
|
+
return (data.statuses || []).slice(0, count).map(s => {
|
|
34
|
+
const u = s.user || {};
|
|
35
|
+
const item = {
|
|
36
|
+
author: u.screen_name || '',
|
|
37
|
+
text: (s.text_raw || strip(s.text || '')).substring(0, 200),
|
|
38
|
+
reposts: s.reposts_count || 0,
|
|
39
|
+
comments: s.comments_count || 0,
|
|
40
|
+
likes: s.attitudes_count || 0,
|
|
41
|
+
time: s.created_at || '',
|
|
42
|
+
url: 'https://weibo.com/' + (u.id || '') + '/' + (s.mblogid || ''),
|
|
43
|
+
};
|
|
44
|
+
if (s.retweeted_status) {
|
|
45
|
+
const rt = s.retweeted_status;
|
|
46
|
+
item.retweeted = (rt.user?.screen_name || '[deleted]') + ': ' + (rt.text_raw || strip(rt.text || '')).substring(0, 100);
|
|
47
|
+
}
|
|
48
|
+
return item;
|
|
49
|
+
});
|
|
50
|
+
})()
|
|
51
|
+
`);
|
|
52
|
+
if (!Array.isArray(data))
|
|
53
|
+
return [];
|
|
54
|
+
return data;
|
|
55
|
+
},
|
|
56
|
+
});
|
package/dist/clis/weibo/hot.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weibo me — current logged-in user profile.
|
|
3
|
+
*/
|
|
4
|
+
import { cli, Strategy } from '../../registry.js';
|
|
5
|
+
import { CommandExecutionError } from '../../errors.js';
|
|
6
|
+
import { getSelfUid } from './utils.js';
|
|
7
|
+
cli({
|
|
8
|
+
site: 'weibo',
|
|
9
|
+
name: 'me',
|
|
10
|
+
description: 'My Weibo profile info',
|
|
11
|
+
domain: 'weibo.com',
|
|
12
|
+
strategy: Strategy.COOKIE,
|
|
13
|
+
args: [],
|
|
14
|
+
columns: ['screen_name', 'uid', 'followers', 'following', 'statuses', 'verified', 'location'],
|
|
15
|
+
func: async (page) => {
|
|
16
|
+
await page.goto('https://weibo.com');
|
|
17
|
+
await page.wait(2);
|
|
18
|
+
const uid = await getSelfUid(page);
|
|
19
|
+
const data = await page.evaluate(`
|
|
20
|
+
(async () => {
|
|
21
|
+
const uid = ${JSON.stringify(uid)};
|
|
22
|
+
|
|
23
|
+
// Try Vue store first
|
|
24
|
+
const app = document.querySelector('#app')?.__vue_app__;
|
|
25
|
+
const store = app?.config?.globalProperties?.$store;
|
|
26
|
+
const cfg = store?.state?.config?.config;
|
|
27
|
+
const u = cfg?.user;
|
|
28
|
+
|
|
29
|
+
// Fetch detail info
|
|
30
|
+
const detailResp = await fetch('/ajax/profile/detail?uid=' + uid, {credentials: 'include'});
|
|
31
|
+
const detail = detailResp.ok ? await detailResp.json() : null;
|
|
32
|
+
const d = detail?.data || {};
|
|
33
|
+
|
|
34
|
+
if (u && u.id) {
|
|
35
|
+
return {
|
|
36
|
+
screen_name: u.screen_name,
|
|
37
|
+
uid: u.id,
|
|
38
|
+
followers: u.followers_count,
|
|
39
|
+
following: u.friends_count,
|
|
40
|
+
statuses: u.statuses_count,
|
|
41
|
+
verified: u.verified || false,
|
|
42
|
+
location: u.location || '',
|
|
43
|
+
description: u.description || d.description || '',
|
|
44
|
+
avatar: u.avatar_hd || u.avatar_large || '',
|
|
45
|
+
profile_url: 'https://weibo.com' + (u.profile_url || '/u/' + u.id),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Fallback: fetch profile API
|
|
50
|
+
const resp = await fetch('/ajax/profile/info?uid=' + uid, {credentials: 'include'});
|
|
51
|
+
if (!resp.ok) return {error: 'HTTP ' + resp.status};
|
|
52
|
+
const info = await resp.json();
|
|
53
|
+
if (!info.ok) return {error: 'API error'};
|
|
54
|
+
const p = info.data?.user;
|
|
55
|
+
if (!p) return {error: 'User data not found'};
|
|
56
|
+
return {
|
|
57
|
+
screen_name: p.screen_name,
|
|
58
|
+
uid: p.id,
|
|
59
|
+
followers: p.followers_count,
|
|
60
|
+
following: p.friends_count,
|
|
61
|
+
statuses: p.statuses_count,
|
|
62
|
+
verified: p.verified || false,
|
|
63
|
+
location: p.location || '',
|
|
64
|
+
description: p.description || d.description || '',
|
|
65
|
+
avatar: p.avatar_hd || p.avatar_large || '',
|
|
66
|
+
profile_url: 'https://weibo.com' + (p.profile_url || '/u/' + p.id),
|
|
67
|
+
};
|
|
68
|
+
})()
|
|
69
|
+
`);
|
|
70
|
+
if (!data || typeof data !== 'object')
|
|
71
|
+
throw new CommandExecutionError('Failed to fetch profile');
|
|
72
|
+
if (data.error)
|
|
73
|
+
throw new CommandExecutionError(String(data.error));
|
|
74
|
+
return data;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weibo post — get a single post by ID.
|
|
3
|
+
*/
|
|
4
|
+
import { cli, Strategy } from '../../registry.js';
|
|
5
|
+
import { CommandExecutionError } from '../../errors.js';
|
|
6
|
+
cli({
|
|
7
|
+
site: 'weibo',
|
|
8
|
+
name: 'post',
|
|
9
|
+
description: 'Get a single Weibo post',
|
|
10
|
+
domain: 'weibo.com',
|
|
11
|
+
strategy: Strategy.COOKIE,
|
|
12
|
+
args: [
|
|
13
|
+
{ name: 'id', required: true, positional: true, help: 'Post ID (numeric idstr or mblogid from URL)' },
|
|
14
|
+
],
|
|
15
|
+
columns: ['field', 'value'],
|
|
16
|
+
func: async (page, kwargs) => {
|
|
17
|
+
await page.goto('https://weibo.com');
|
|
18
|
+
await page.wait(2);
|
|
19
|
+
const id = String(kwargs.id);
|
|
20
|
+
const data = await page.evaluate(`
|
|
21
|
+
(async () => {
|
|
22
|
+
const id = ${JSON.stringify(id)};
|
|
23
|
+
const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').trim();
|
|
24
|
+
|
|
25
|
+
const resp = await fetch('/ajax/statuses/show?id=' + encodeURIComponent(id), {credentials: 'include'});
|
|
26
|
+
if (!resp.ok) return {error: 'HTTP ' + resp.status};
|
|
27
|
+
const s = await resp.json();
|
|
28
|
+
if (!s.ok && !s.idstr) return {error: 'Post not found'};
|
|
29
|
+
|
|
30
|
+
// Fetch long text if needed
|
|
31
|
+
let fullText = s.text_raw || strip(s.text || '');
|
|
32
|
+
if (s.isLongText || s.is_long_text) {
|
|
33
|
+
try {
|
|
34
|
+
const ltResp = await fetch('/ajax/statuses/longtext?id=' + s.idstr, {credentials: 'include'});
|
|
35
|
+
if (ltResp.ok) {
|
|
36
|
+
const lt = await ltResp.json();
|
|
37
|
+
if (lt.data?.longTextContent) fullText = strip(lt.data.longTextContent);
|
|
38
|
+
}
|
|
39
|
+
} catch {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const u = s.user || {};
|
|
43
|
+
const result = {
|
|
44
|
+
id: s.idstr || String(s.id),
|
|
45
|
+
mblogid: s.mblogid,
|
|
46
|
+
author: u.screen_name || '',
|
|
47
|
+
text: fullText,
|
|
48
|
+
created_at: s.created_at,
|
|
49
|
+
source: strip(s.source || ''),
|
|
50
|
+
reposts: s.reposts_count || 0,
|
|
51
|
+
comments: s.comments_count || 0,
|
|
52
|
+
likes: s.attitudes_count || 0,
|
|
53
|
+
pic_count: s.pic_num || 0,
|
|
54
|
+
url: 'https://weibo.com/' + (u.id || '') + '/' + (s.mblogid || ''),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
if (s.retweeted_status) {
|
|
58
|
+
const rt = s.retweeted_status;
|
|
59
|
+
result.retweeted_from = (rt.user?.screen_name || '[deleted]');
|
|
60
|
+
result.retweeted_text = rt.text_raw || strip(rt.text || '');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return result;
|
|
64
|
+
})()
|
|
65
|
+
`);
|
|
66
|
+
if (!data || typeof data !== 'object')
|
|
67
|
+
throw new CommandExecutionError('Failed to fetch post');
|
|
68
|
+
if (data.error)
|
|
69
|
+
throw new CommandExecutionError(String(data.error));
|
|
70
|
+
return Object.entries(data).map(([field, value]) => ({
|
|
71
|
+
field,
|
|
72
|
+
value: String(value),
|
|
73
|
+
}));
|
|
74
|
+
},
|
|
75
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weibo user — get user profile by uid or screen_name.
|
|
3
|
+
*/
|
|
4
|
+
import { cli, Strategy } from '../../registry.js';
|
|
5
|
+
import { CommandExecutionError } from '../../errors.js';
|
|
6
|
+
cli({
|
|
7
|
+
site: 'weibo',
|
|
8
|
+
name: 'user',
|
|
9
|
+
description: 'Get Weibo user profile',
|
|
10
|
+
domain: 'weibo.com',
|
|
11
|
+
strategy: Strategy.COOKIE,
|
|
12
|
+
args: [
|
|
13
|
+
{ name: 'id', required: true, positional: true, help: 'User ID (numeric uid) or screen name' },
|
|
14
|
+
],
|
|
15
|
+
columns: ['screen_name', 'uid', 'followers', 'following', 'statuses', 'verified', 'description', 'location', 'url'],
|
|
16
|
+
func: async (page, kwargs) => {
|
|
17
|
+
await page.goto('https://weibo.com');
|
|
18
|
+
await page.wait(2);
|
|
19
|
+
const id = String(kwargs.id);
|
|
20
|
+
const data = await page.evaluate(`
|
|
21
|
+
(async () => {
|
|
22
|
+
const id = ${JSON.stringify(id)};
|
|
23
|
+
const isUid = /^\\d+$/.test(id);
|
|
24
|
+
const query = isUid ? 'uid=' + id : 'screen_name=' + encodeURIComponent(id);
|
|
25
|
+
|
|
26
|
+
const resp = await fetch('/ajax/profile/info?' + query, {credentials: 'include'});
|
|
27
|
+
if (!resp.ok) return {error: 'HTTP ' + resp.status};
|
|
28
|
+
const data = await resp.json();
|
|
29
|
+
if (!data.ok || !data.data?.user) return {error: 'User not found'};
|
|
30
|
+
|
|
31
|
+
const u = data.data.user;
|
|
32
|
+
|
|
33
|
+
// Fetch detail info
|
|
34
|
+
const detailResp = await fetch('/ajax/profile/detail?uid=' + u.id, {credentials: 'include'});
|
|
35
|
+
const detail = detailResp.ok ? await detailResp.json() : null;
|
|
36
|
+
const d = detail?.data || {};
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
screen_name: u.screen_name,
|
|
40
|
+
uid: u.id,
|
|
41
|
+
followers: u.followers_count,
|
|
42
|
+
following: u.friends_count,
|
|
43
|
+
statuses: u.statuses_count,
|
|
44
|
+
verified: u.verified || false,
|
|
45
|
+
verified_reason: u.verified_reason || '',
|
|
46
|
+
description: u.description || d.description || '',
|
|
47
|
+
location: u.location || '',
|
|
48
|
+
gender: u.gender === 'm' ? 'male' : u.gender === 'f' ? 'female' : '',
|
|
49
|
+
avatar: u.avatar_hd || u.avatar_large || '',
|
|
50
|
+
url: 'https://weibo.com' + (u.profile_url || '/u/' + u.id),
|
|
51
|
+
birthday: d.birthday || '',
|
|
52
|
+
created_at: d.created_at || '',
|
|
53
|
+
ip_location: d.ip_location || '',
|
|
54
|
+
};
|
|
55
|
+
})()
|
|
56
|
+
`);
|
|
57
|
+
if (!data || typeof data !== 'object')
|
|
58
|
+
throw new CommandExecutionError('Failed to fetch user profile');
|
|
59
|
+
if (data.error)
|
|
60
|
+
throw new CommandExecutionError(String(data.error));
|
|
61
|
+
return data;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Weibo utilities — uid extraction.
|
|
3
|
+
*/
|
|
4
|
+
import { AuthRequiredError } from '../../errors.js';
|
|
5
|
+
/** Get the currently logged-in user's uid from Vue store or config API. */
|
|
6
|
+
export async function getSelfUid(page) {
|
|
7
|
+
const uid = await page.evaluate(`
|
|
8
|
+
(() => {
|
|
9
|
+
const app = document.querySelector('#app')?.__vue_app__;
|
|
10
|
+
const store = app?.config?.globalProperties?.$store;
|
|
11
|
+
const uid = store?.state?.config?.config?.uid;
|
|
12
|
+
if (uid) return String(uid);
|
|
13
|
+
return null;
|
|
14
|
+
})()
|
|
15
|
+
`);
|
|
16
|
+
if (uid)
|
|
17
|
+
return uid;
|
|
18
|
+
// Fallback: config API
|
|
19
|
+
const config = await page.evaluate(`
|
|
20
|
+
(async () => {
|
|
21
|
+
const resp = await fetch('/ajax/config/get_config', {credentials: 'include'});
|
|
22
|
+
if (!resp.ok) return null;
|
|
23
|
+
const data = await resp.json();
|
|
24
|
+
return data.ok && data.data?.uid ? String(data.data.uid) : null;
|
|
25
|
+
})()
|
|
26
|
+
`);
|
|
27
|
+
if (config)
|
|
28
|
+
return config;
|
|
29
|
+
throw new AuthRequiredError('weibo.com');
|
|
30
|
+
}
|
|
@@ -10,3 +10,20 @@
|
|
|
10
10
|
* Normalize a pasted WeChat article URL.
|
|
11
11
|
*/
|
|
12
12
|
export declare function normalizeWechatUrl(raw: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Format a WeChat article timestamp as a UTC+8 datetime string.
|
|
15
|
+
* Accepts either Unix seconds or milliseconds.
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatWechatTimestamp(rawTimestamp: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Extract the raw create_time value from supported WeChat inline script formats.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractWechatCreateTimeValue(htmlStr: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Extract the publish time from DOM text first, then fall back to numeric create_time values.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractWechatPublishTime(publishTimeText: string | null | undefined, htmlStr: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Build a self-contained helper for execution inside page.evaluate().
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildExtractWechatPublishTimeJs(): string;
|