publishport-opencli 1.0.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/LICENSE +214 -0
- package/README.md +299 -0
- package/README.zh-CN.md +341 -0
- package/cli-manifest.json +51513 -0
- package/clis/12306/auth.js +25 -0
- package/clis/12306/me.js +9 -0
- package/clis/12306/orders.js +13 -0
- package/clis/12306/passengers.js +14 -0
- package/clis/12306/price.js +1 -0
- package/clis/12306/stations.js +1 -0
- package/clis/12306/train.js +1 -0
- package/clis/12306/trains.js +1 -0
- package/clis/12306/utils.js +1 -0
- package/clis/1688/assets.js +127 -0
- package/clis/1688/auth.js +1 -0
- package/clis/1688/download.js +1 -0
- package/clis/1688/item.js +21 -0
- package/clis/1688/search.js +129 -0
- package/clis/1688/shared.js +11 -0
- package/clis/1688/store.js +26 -0
- package/clis/1point3acres/auth.js +18 -0
- package/clis/1point3acres/digest.js +1 -0
- package/clis/1point3acres/forum.js +1 -0
- package/clis/1point3acres/forums.js +1 -0
- package/clis/1point3acres/hot.js +1 -0
- package/clis/1point3acres/latest.js +1 -0
- package/clis/1point3acres/notifications.js +1 -0
- package/clis/1point3acres/search.js +1 -0
- package/clis/1point3acres/thread.js +2 -0
- package/clis/1point3acres/user.js +1 -0
- package/clis/1point3acres/utils.js +6 -0
- package/clis/36kr/article.js +20 -0
- package/clis/36kr/hot.js +16 -0
- package/clis/36kr/news.js +1 -0
- package/clis/36kr/search.js +36 -0
- package/clis/51job/company.js +36 -0
- package/clis/51job/detail.js +35 -0
- package/clis/51job/hot.js +1 -0
- package/clis/51job/search.js +1 -0
- package/clis/51job/utils.js +21 -0
- package/clis/_atlassian/shared.js +21 -0
- package/clis/_shared/article/auth.js +16 -0
- package/clis/_shared/article/douban-md2draft.js +4 -0
- package/clis/_shared/article/format.js +1 -0
- package/clis/_shared/article/images.js +95 -0
- package/clis/_shared/article/login.js +1 -0
- package/clis/_shared/article/page-runtime.js +745 -0
- package/clis/_shared/article/publish.js +34 -0
- package/clis/_shared/common.js +1 -0
- package/clis/_shared/content-guard.js +1 -0
- package/clis/_shared/desktop-commands.js +1 -0
- package/clis/_shared/search-adapter.js +1 -0
- package/clis/_shared/site-auth.js +1 -0
- package/clis/_shared/token-auth.js +2 -0
- package/clis/_shared/video-publish.js +62 -0
- package/clis/aibase/news.js +31 -0
- package/clis/amazon/auth.js +19 -0
- package/clis/amazon/bestsellers.js +1 -0
- package/clis/amazon/discussion.js +20 -0
- package/clis/amazon/movers-shakers.js +1 -0
- package/clis/amazon/new-releases.js +1 -0
- package/clis/amazon/offer.js +29 -0
- package/clis/amazon/product.js +25 -0
- package/clis/amazon/rankings.js +68 -0
- package/clis/amazon/search.js +16 -0
- package/clis/amazon/shared.js +11 -0
- package/clis/antigravity/SKILL.md +38 -0
- package/clis/antigravity/_actions.js +201 -0
- package/clis/antigravity/audit-extras.js +78 -0
- package/clis/antigravity/delete.js +1 -0
- package/clis/antigravity/dump.js +1 -0
- package/clis/antigravity/extract-code.js +18 -0
- package/clis/antigravity/history.js +1 -0
- package/clis/antigravity/mark-read.js +1 -0
- package/clis/antigravity/model.js +90 -0
- package/clis/antigravity/new.js +9 -0
- package/clis/antigravity/read.js +10 -0
- package/clis/antigravity/rename.js +1 -0
- package/clis/antigravity/send.js +11 -0
- package/clis/antigravity/serve.js +97 -0
- package/clis/antigravity/status.js +1 -0
- package/clis/antigravity/storage.js +12 -0
- package/clis/antigravity/watch.js +6 -0
- package/clis/apple-podcasts/episodes.js +1 -0
- package/clis/apple-podcasts/search.js +1 -0
- package/clis/apple-podcasts/top.js +1 -0
- package/clis/apple-podcasts/utils.js +1 -0
- package/clis/archive/item.js +1 -0
- package/clis/archive/search.js +1 -0
- package/clis/archive/snapshots.js +1 -0
- package/clis/archive/wayback.js +1 -0
- package/clis/arxiv/author.js +1 -0
- package/clis/arxiv/paper.js +1 -0
- package/clis/arxiv/recent.js +1 -0
- package/clis/arxiv/search.js +1 -0
- package/clis/arxiv/utils.js +1 -0
- package/clis/autohome/__fixtures__/catalog.html +11 -0
- package/clis/autohome/__fixtures__/koubei.json +116 -0
- package/clis/autohome/brand.js +1 -0
- package/clis/autohome/score.js +1 -0
- package/clis/autohome/utils.js +1 -0
- package/clis/baidu-scholar/search.js +63 -0
- package/clis/baijiahao/article.js +1 -0
- package/clis/baijiahao/lists.js +1 -0
- package/clis/baijiahao/login.js +1 -0
- package/clis/baijiahao/publish.js +57 -0
- package/clis/baijiahao/stats.js +1 -0
- package/clis/baijiahao/whoami.js +1 -0
- package/clis/band/auth.js +29 -0
- package/clis/band/bands.js +37 -0
- package/clis/band/mentions.js +6 -0
- package/clis/band/post.js +76 -0
- package/clis/band/posts.js +50 -0
- package/clis/barchart/flow.js +70 -0
- package/clis/barchart/greeks.js +88 -0
- package/clis/barchart/options.js +61 -0
- package/clis/barchart/quote.js +87 -0
- package/clis/bbc/news.js +1 -0
- package/clis/bbc/topic.js +1 -0
- package/clis/bbc/utils.js +1 -0
- package/clis/bilibili/article.js +1 -0
- package/clis/bilibili/auth.js +1 -0
- package/clis/bilibili/categories.js +1 -0
- package/clis/bilibili/coin.js +1 -0
- package/clis/bilibili/comment-del.js +1 -0
- package/clis/bilibili/comment-like.js +1 -0
- package/clis/bilibili/comment.js +1 -0
- package/clis/bilibili/comments.js +1 -0
- package/clis/bilibili/dm-list.js +1 -0
- package/clis/bilibili/dm-read.js +1 -0
- package/clis/bilibili/dm-send.js +1 -0
- package/clis/bilibili/download.js +7 -0
- package/clis/bilibili/drafts.js +1 -0
- package/clis/bilibili/dynamic-post.js +24 -0
- package/clis/bilibili/dynamic.js +1 -0
- package/clis/bilibili/favorite-add.js +1 -0
- package/clis/bilibili/favorite.js +1 -0
- package/clis/bilibili/feed.js +2 -0
- package/clis/bilibili/follow.js +1 -0
- package/clis/bilibili/following.js +1 -0
- package/clis/bilibili/history.js +1 -0
- package/clis/bilibili/hot.js +15 -0
- package/clis/bilibili/like.js +1 -0
- package/clis/bilibili/me.js +1 -0
- package/clis/bilibili/partitions.js +1 -0
- package/clis/bilibili/ranking.js +1 -0
- package/clis/bilibili/search.js +1 -0
- package/clis/bilibili/stats.js +1 -0
- package/clis/bilibili/subtitle.js +21 -0
- package/clis/bilibili/summary.js +1 -0
- package/clis/bilibili/topics.js +1 -0
- package/clis/bilibili/unfollow.js +1 -0
- package/clis/bilibili/upload.js +1 -0
- package/clis/bilibili/user-videos.js +1 -0
- package/clis/bilibili/utils.js +50 -0
- package/clis/bilibili/video-drafts.js +1 -0
- package/clis/bilibili/video.js +1 -0
- package/clis/binance/asks.js +1 -0
- package/clis/binance/depth.js +1 -0
- package/clis/binance/gainers.js +1 -0
- package/clis/binance/klines.js +1 -0
- package/clis/binance/losers.js +1 -0
- package/clis/binance/pairs.js +1 -0
- package/clis/binance/price.js +1 -0
- package/clis/binance/prices.js +1 -0
- package/clis/binance/ticker.js +1 -0
- package/clis/binance/top.js +1 -0
- package/clis/binance/trades.js +1 -0
- package/clis/bloomberg/businessweek.js +12 -0
- package/clis/bloomberg/crypto.js +1 -0
- package/clis/bloomberg/economics.js +1 -0
- package/clis/bloomberg/feeds.js +1 -0
- package/clis/bloomberg/green.js +1 -0
- package/clis/bloomberg/industries.js +1 -0
- package/clis/bloomberg/main.js +1 -0
- package/clis/bloomberg/markets.js +1 -0
- package/clis/bloomberg/news.js +57 -0
- package/clis/bloomberg/opinions.js +1 -0
- package/clis/bloomberg/politics.js +1 -0
- package/clis/bloomberg/pursuits.js +1 -0
- package/clis/bloomberg/tech.js +1 -0
- package/clis/bloomberg/utils.js +17 -0
- package/clis/bluesky/post.js +1 -0
- package/clis/bluesky/shared.js +56 -0
- package/clis/bluesky/whoami.js +1 -0
- package/clis/booking/search.js +93 -0
- package/clis/boss/auth.js +13 -0
- package/clis/boss/batchgreet.js +1 -0
- package/clis/boss/chatlist.js +1 -0
- package/clis/boss/chatmsg.js +1 -0
- package/clis/boss/detail.js +1 -0
- package/clis/boss/exchange.js +1 -0
- package/clis/boss/greet.js +1 -0
- package/clis/boss/invite.js +4 -0
- package/clis/boss/joblist.js +1 -0
- package/clis/boss/mark.js +1 -0
- package/clis/boss/recommend.js +1 -0
- package/clis/boss/resume.js +90 -0
- package/clis/boss/search.js +1 -0
- package/clis/boss/send.js +1 -0
- package/clis/boss/stats.js +1 -0
- package/clis/boss/utils.js +161 -0
- package/clis/brave/search.js +23 -0
- package/clis/chaoxing/assignments.js +1 -0
- package/clis/chaoxing/auth.js +21 -0
- package/clis/chaoxing/exams.js +1 -0
- package/clis/chaoxing/utils.js +58 -0
- package/clis/chatgpt/ask.js +1 -0
- package/clis/chatgpt/auth.js +17 -0
- package/clis/chatgpt/detail.js +1 -0
- package/clis/chatgpt/history.js +1 -0
- package/clis/chatgpt/image.js +1 -0
- package/clis/chatgpt/model.js +1 -0
- package/clis/chatgpt/new.js +1 -0
- package/clis/chatgpt/project-file-add.js +1 -0
- package/clis/chatgpt/project-list.js +1 -0
- package/clis/chatgpt/read.js +1 -0
- package/clis/chatgpt/send.js +1 -0
- package/clis/chatgpt/status.js +1 -0
- package/clis/chatgpt/utils.js +1092 -0
- package/clis/chatgpt-app/ask.js +1 -0
- package/clis/chatgpt-app/ax.js +512 -0
- package/clis/chatgpt-app/model.js +1 -0
- package/clis/chatgpt-app/new.js +1 -0
- package/clis/chatgpt-app/read.js +1 -0
- package/clis/chatgpt-app/send.js +1 -0
- package/clis/chatgpt-app/status.js +1 -0
- package/clis/chatwise/ask.js +1 -0
- package/clis/chatwise/export.js +21 -0
- package/clis/chatwise/history.js +26 -0
- package/clis/chatwise/model.js +48 -0
- package/clis/chatwise/new.js +1 -0
- package/clis/chatwise/read.js +25 -0
- package/clis/chatwise/screenshot.js +1 -0
- package/clis/chatwise/send.js +1 -0
- package/clis/chatwise/status.js +1 -0
- package/clis/chatwise/utils.js +75 -0
- package/clis/chess/analyze.js +1 -0
- package/clis/chess/game.js +1 -0
- package/clis/chess/games.js +1 -0
- package/clis/chess/stats.js +1 -0
- package/clis/chess/utils.js +1 -0
- package/clis/claude/ask.js +4 -0
- package/clis/claude/auth.js +19 -0
- package/clis/claude/detail.js +1 -0
- package/clis/claude/history.js +1 -0
- package/clis/claude/new.js +1 -0
- package/clis/claude/read.js +1 -0
- package/clis/claude/send.js +1 -0
- package/clis/claude/status.js +1 -0
- package/clis/claude/utils.js +179 -0
- package/clis/cnblogs/article.js +1 -0
- package/clis/cnblogs/login.js +1 -0
- package/clis/cnblogs/stats.js +37 -0
- package/clis/cnblogs/whoami.js +1 -0
- package/clis/cnki/search.js +38 -0
- package/clis/codex/_actions.js +72 -0
- package/clis/codex/archive.js +1 -0
- package/clis/codex/ask.js +23 -0
- package/clis/codex/dump.js +1 -0
- package/clis/codex/export.js +12 -0
- package/clis/codex/extract-diff.js +28 -0
- package/clis/codex/history.js +1 -0
- package/clis/codex/model.js +118 -0
- package/clis/codex/new.js +1 -0
- package/clis/codex/pin.js +1 -0
- package/clis/codex/projects.js +1 -0
- package/clis/codex/read.js +16 -0
- package/clis/codex/rename.js +34 -0
- package/clis/codex/screenshot.js +1 -0
- package/clis/codex/send.js +16 -0
- package/clis/codex/sidebar.js +1 -0
- package/clis/codex/status.js +1 -0
- package/clis/coingecko/categories.js +1 -0
- package/clis/coingecko/coin.js +1 -0
- package/clis/coingecko/derivatives.js +1 -0
- package/clis/coingecko/exchanges.js +1 -0
- package/clis/coingecko/global.js +1 -0
- package/clis/coingecko/top.js +1 -0
- package/clis/coingecko/trending.js +1 -0
- package/clis/confluence/create.js +1 -0
- package/clis/confluence/page.js +1 -0
- package/clis/confluence/search.js +1 -0
- package/clis/confluence/shared.js +1 -0
- package/clis/confluence/update.js +1 -0
- package/clis/coupang/add-to-cart.js +86 -0
- package/clis/coupang/auth.js +16 -0
- package/clis/coupang/product.js +161 -0
- package/clis/coupang/search.js +385 -0
- package/clis/coupang/utils.js +1 -0
- package/clis/crates/crate.js +1 -0
- package/clis/crates/search.js +1 -0
- package/clis/crates/utils.js +1 -0
- package/clis/csdn/article.js +29 -0
- package/clis/csdn/columns.js +1 -0
- package/clis/csdn/login.js +1 -0
- package/clis/csdn/stats.js +71 -0
- package/clis/csdn/whoami.js +1 -0
- package/clis/cto51/article.js +1 -0
- package/clis/cto51/whoami.js +1 -0
- package/clis/ctrip/auth.js +1 -0
- package/clis/ctrip/flight.js +17 -0
- package/clis/ctrip/hotel-search.js +24 -0
- package/clis/ctrip/hotel-suggest.js +1 -0
- package/clis/ctrip/search.js +1 -0
- package/clis/ctrip/utils.js +119 -0
- package/clis/cursor/ask.js +22 -0
- package/clis/cursor/composer.js +12 -0
- package/clis/cursor/dump.js +1 -0
- package/clis/cursor/export.js +23 -0
- package/clis/cursor/extract-code.js +18 -0
- package/clis/cursor/history.js +26 -0
- package/clis/cursor/model.js +18 -0
- package/clis/cursor/new.js +1 -0
- package/clis/cursor/read.js +27 -0
- package/clis/cursor/screenshot.js +1 -0
- package/clis/cursor/send.js +12 -0
- package/clis/cursor/status.js +1 -0
- package/clis/dblp/author.js +1 -0
- package/clis/dblp/paper.js +1 -0
- package/clis/dblp/search.js +1 -0
- package/clis/dblp/utils.js +1 -0
- package/clis/dblp/venue.js +1 -0
- package/clis/deepseek/ask.js +1 -0
- package/clis/deepseek/auth.js +22 -0
- package/clis/deepseek/detail.js +1 -0
- package/clis/deepseek/history.js +1 -0
- package/clis/deepseek/new.js +1 -0
- package/clis/deepseek/read.js +1 -0
- package/clis/deepseek/send.js +50 -0
- package/clis/deepseek/status.js +1 -0
- package/clis/deepseek/utils.js +202 -0
- package/clis/defillama/protocol.js +1 -0
- package/clis/defillama/protocols.js +1 -0
- package/clis/defillama/utils.js +1 -0
- package/clis/devto/auth.js +9 -0
- package/clis/devto/latest.js +1 -0
- package/clis/devto/publish.js +17 -0
- package/clis/devto/read.js +3 -0
- package/clis/devto/stats.js +27 -0
- package/clis/devto/tag.js +1 -0
- package/clis/devto/top.js +1 -0
- package/clis/devto/user.js +1 -0
- package/clis/dianping/__fixtures__/search.html +168 -0
- package/clis/dianping/__fixtures__/shop.html +6 -0
- package/clis/dianping/auth.js +10 -0
- package/clis/dianping/cityResolver.js +4 -0
- package/clis/dianping/search.js +1 -0
- package/clis/dianping/shop.js +1 -0
- package/clis/dianping/utils.js +1 -0
- package/clis/dictionary/examples.js +1 -0
- package/clis/dictionary/search.js +1 -0
- package/clis/dictionary/synonyms.js +1 -0
- package/clis/discord-app/channels.js +1 -0
- package/clis/discord-app/delete.js +66 -0
- package/clis/discord-app/goto.js +1 -0
- package/clis/discord-app/members.js +21 -0
- package/clis/discord-app/read.js +1 -0
- package/clis/discord-app/search.js +29 -0
- package/clis/discord-app/send.js +10 -0
- package/clis/discord-app/servers.js +1 -0
- package/clis/discord-app/status.js +1 -0
- package/clis/discord-app/thread-read.js +1 -0
- package/clis/discord-app/threads.js +1 -0
- package/clis/discord-app/utils.js +226 -0
- package/clis/discord-web/auth.js +10 -0
- package/clis/discord-web/send.js +13 -0
- package/clis/dockerhub/image.js +1 -0
- package/clis/dockerhub/search.js +1 -0
- package/clis/dockerhub/utils.js +1 -0
- package/clis/dongchedi/__fixtures__/search.json +60 -0
- package/clis/dongchedi/__fixtures__/series-detail.json +911 -0
- package/clis/dongchedi/__fixtures__/series-score.json +1396 -0
- package/clis/dongchedi/koubei.js +1 -0
- package/clis/dongchedi/models.js +1 -0
- package/clis/dongchedi/score.js +1 -0
- package/clis/dongchedi/search.js +1 -0
- package/clis/dongchedi/series.js +1 -0
- package/clis/dongchedi/specs.js +1 -0
- package/clis/dongchedi/utils.js +1 -0
- package/clis/douban/article.js +53 -0
- package/clis/douban/auth.js +21 -0
- package/clis/douban/book-hot.js +1 -0
- package/clis/douban/download.js +1 -0
- package/clis/douban/marks.js +59 -0
- package/clis/douban/movie-hot.js +1 -0
- package/clis/douban/photos.js +1 -0
- package/clis/douban/reviews.js +53 -0
- package/clis/douban/search.js +1 -0
- package/clis/douban/subject.js +1 -0
- package/clis/douban/top250.js +50 -0
- package/clis/douban/utils.js +344 -0
- package/clis/doubao/ask.js +1 -0
- package/clis/doubao/auth.js +28 -0
- package/clis/doubao/detail.js +1 -0
- package/clis/doubao/history.js +1 -0
- package/clis/doubao/meeting-summary.js +1 -0
- package/clis/doubao/meeting-transcript.js +1 -0
- package/clis/doubao/new.js +1 -0
- package/clis/doubao/read.js +1 -0
- package/clis/doubao/send.js +1 -0
- package/clis/doubao/status.js +1 -0
- package/clis/doubao/utils.js +704 -0
- package/clis/doubao-app/ask.js +1 -0
- package/clis/doubao-app/dump.js +1 -0
- package/clis/doubao-app/new.js +1 -0
- package/clis/doubao-app/read.js +1 -0
- package/clis/doubao-app/screenshot.js +1 -0
- package/clis/doubao-app/send.js +1 -0
- package/clis/doubao-app/status.js +1 -0
- package/clis/doubao-app/utils.js +64 -0
- package/clis/douyin/_shared/browser-fetch.js +28 -0
- package/clis/douyin/_shared/creation-id.js +1 -0
- package/clis/douyin/_shared/evaluate-result.js +1 -0
- package/clis/douyin/_shared/imagex-upload.js +1 -0
- package/clis/douyin/_shared/public-api.js +1 -0
- package/clis/douyin/_shared/sts2.js +1 -0
- package/clis/douyin/_shared/text-extra.js +1 -0
- package/clis/douyin/_shared/timing.js +1 -0
- package/clis/douyin/_shared/tos-upload.js +5 -0
- package/clis/douyin/_shared/transcode.js +1 -0
- package/clis/douyin/_shared/types.js +1 -0
- package/clis/douyin/_shared/vod-upload.js +5 -0
- package/clis/douyin/activities.js +1 -0
- package/clis/douyin/auth.js +1 -0
- package/clis/douyin/collections.js +1 -0
- package/clis/douyin/delete.js +76 -0
- package/clis/douyin/draft.js +166 -0
- package/clis/douyin/drafts.js +1 -0
- package/clis/douyin/hashtag.js +1 -0
- package/clis/douyin/location.js +1 -0
- package/clis/douyin/profile.js +1 -0
- package/clis/douyin/publish-image.js +64 -0
- package/clis/douyin/publish.js +14 -0
- package/clis/douyin/search.js +51 -0
- package/clis/douyin/stats.js +1 -0
- package/clis/douyin/update.js +1 -0
- package/clis/douyin/user-videos.js +1 -0
- package/clis/douyin/videos.js +1 -0
- package/clis/duckduckgo/search.js +1 -0
- package/clis/duckduckgo/suggest.js +1 -0
- package/clis/eastmoney/_secid.js +1 -0
- package/clis/eastmoney/announcement.js +1 -0
- package/clis/eastmoney/article.js +1 -0
- package/clis/eastmoney/convertible.js +1 -0
- package/clis/eastmoney/etf.js +1 -0
- package/clis/eastmoney/holders.js +1 -0
- package/clis/eastmoney/hot-rank.js +30 -0
- package/clis/eastmoney/index-board.js +1 -0
- package/clis/eastmoney/kline.js +1 -0
- package/clis/eastmoney/kuaixun.js +1 -0
- package/clis/eastmoney/longhu.js +1 -0
- package/clis/eastmoney/money-flow.js +1 -0
- package/clis/eastmoney/northbound.js +1 -0
- package/clis/eastmoney/quote.js +1 -0
- package/clis/eastmoney/rank.js +1 -0
- package/clis/eastmoney/sectors.js +1 -0
- package/clis/eastmoney/whoami.js +1 -0
- package/clis/endoflife/product.js +1 -0
- package/clis/endoflife/utils.js +1 -0
- package/clis/facebook/__fixtures__/notifications-page.html +13 -0
- package/clis/facebook/add-friend.js +24 -0
- package/clis/facebook/auth.js +1 -0
- package/clis/facebook/comment.js +18 -0
- package/clis/facebook/events.js +26 -0
- package/clis/facebook/feed.js +209 -0
- package/clis/facebook/friends.js +24 -0
- package/clis/facebook/groups.js +32 -0
- package/clis/facebook/join-group.js +25 -0
- package/clis/facebook/like.js +29 -0
- package/clis/facebook/marketplace-inbox.js +34 -0
- package/clis/facebook/marketplace-listings.js +33 -0
- package/clis/facebook/memories.js +21 -0
- package/clis/facebook/notifications.js +31 -0
- package/clis/facebook/post.js +88 -0
- package/clis/facebook/profile.js +18 -0
- package/clis/facebook/search.js +22 -0
- package/clis/flathub/app.js +1 -0
- package/clis/flathub/search.js +1 -0
- package/clis/flathub/utils.js +1 -0
- package/clis/flomo/auth.js +22 -0
- package/clis/flomo/memos.js +13 -0
- package/clis/gemini/ask.js +1 -0
- package/clis/gemini/auth.js +14 -0
- package/clis/gemini/deep-research-result.js +4 -0
- package/clis/gemini/deep-research.js +1 -0
- package/clis/gemini/detail.js +1 -0
- package/clis/gemini/history.js +1 -0
- package/clis/gemini/image.js +3 -0
- package/clis/gemini/new.js +1 -0
- package/clis/gemini/read.js +1 -0
- package/clis/gemini/status.js +1 -0
- package/clis/gemini/utils.js +1234 -0
- package/clis/geogebra/add-circle.js +1 -0
- package/clis/geogebra/add-line.js +1 -0
- package/clis/geogebra/add-point.js +1 -0
- package/clis/geogebra/add-polygon.js +1 -0
- package/clis/geogebra/eval.js +1 -0
- package/clis/geogebra/hexagon.js +1 -0
- package/clis/geogebra/info.js +13 -0
- package/clis/geogebra/list.js +1 -0
- package/clis/geogebra/triangle.js +1 -0
- package/clis/geogebra/utils.js +99 -0
- package/clis/ghost/publish.js +1 -0
- package/clis/ghost/shared.js +1 -0
- package/clis/ghost/whoami.js +1 -0
- package/clis/gitee/auth.js +12 -0
- package/clis/gitee/index.js +1 -0
- package/clis/gitee/search.js +1 -0
- package/clis/gitee/trending.js +240 -0
- package/clis/gitee/user.js +71 -0
- package/clis/github/auth.js +7 -0
- package/clis/github-trending/repos.js +1 -0
- package/clis/google/news.js +1 -0
- package/clis/google/search.js +90 -0
- package/clis/google/suggest.js +1 -0
- package/clis/google/trends.js +1 -0
- package/clis/google/utils.js +1 -0
- package/clis/google-scholar/cite.js +20 -0
- package/clis/google-scholar/profile.js +36 -0
- package/clis/google-scholar/search.js +35 -0
- package/clis/goproxy/module.js +1 -0
- package/clis/goproxy/utils.js +1 -0
- package/clis/goproxy/versions.js +1 -0
- package/clis/gov-law/recent.js +1 -0
- package/clis/gov-law/search.js +14 -0
- package/clis/gov-law/shared.js +29 -0
- package/clis/gov-policy/__fixtures__/recent.html +16 -0
- package/clis/gov-policy/__fixtures__/search.html +41 -0
- package/clis/gov-policy/recent.js +8 -0
- package/clis/gov-policy/search.js +8 -0
- package/clis/gov-policy/utils.js +1 -0
- package/clis/grok/ask.js +1 -0
- package/clis/grok/auth.js +17 -0
- package/clis/grok/delete.js +1 -0
- package/clis/grok/detail.js +1 -0
- package/clis/grok/export-all.js +170 -0
- package/clis/grok/export-utils.js +1 -0
- package/clis/grok/export.js +106 -0
- package/clis/grok/history.js +1 -0
- package/clis/grok/image.js +119 -0
- package/clis/grok/image.test.ts +127 -0
- package/clis/grok/new.js +1 -0
- package/clis/grok/pin.js +1 -0
- package/clis/grok/read.js +1 -0
- package/clis/grok/send.js +1 -0
- package/clis/grok/status.js +1 -0
- package/clis/grok/utils.js +337 -0
- package/clis/guazi/__fixtures__/detail.html +7 -0
- package/clis/guazi/__fixtures__/list.html +5 -0
- package/clis/guazi/browse.js +1 -0
- package/clis/guazi/car.js +1 -0
- package/clis/guazi/utils.js +1 -0
- package/clis/hackernews/ask.js +1 -0
- package/clis/hackernews/auth.js +9 -0
- package/clis/hackernews/best.js +1 -0
- package/clis/hackernews/comment.js +61 -0
- package/clis/hackernews/jobs.js +1 -0
- package/clis/hackernews/new.js +1 -0
- package/clis/hackernews/read.js +11 -0
- package/clis/hackernews/search.js +1 -0
- package/clis/hackernews/show.js +1 -0
- package/clis/hackernews/submit.js +65 -0
- package/clis/hackernews/top.js +1 -0
- package/clis/hackernews/user.js +1 -0
- package/clis/hashnode/auth.js +11 -0
- package/clis/hashnode/publish.js +92 -0
- package/clis/hf/auth.js +12 -0
- package/clis/hf/datasets.js +1 -0
- package/clis/hf/models.js +1 -0
- package/clis/hf/paper.js +1 -0
- package/clis/hf/spaces.js +1 -0
- package/clis/hf/top.js +1 -0
- package/clis/homebrew/cask.js +1 -0
- package/clis/homebrew/formula.js +1 -0
- package/clis/homebrew/popular.js +1 -0
- package/clis/homebrew/utils.js +1 -0
- package/clis/huodongxing/events.js +1 -0
- package/clis/hupu/__fixtures__/hot-home.html +64 -0
- package/clis/hupu/auth.js +14 -0
- package/clis/hupu/detail.js +7 -0
- package/clis/hupu/hot.js +14 -0
- package/clis/hupu/like.js +1 -0
- package/clis/hupu/mentions.js +91 -0
- package/clis/hupu/reply.js +1 -0
- package/clis/hupu/search.js +1 -0
- package/clis/hupu/unlike.js +1 -0
- package/clis/hupu/utils.js +243 -0
- package/clis/imdb/person.js +140 -0
- package/clis/imdb/reviews.js +40 -0
- package/clis/imdb/search.js +113 -0
- package/clis/imdb/title.js +1 -0
- package/clis/imdb/top.js +1 -0
- package/clis/imdb/trending.js +1 -0
- package/clis/imdb/utils.js +162 -0
- package/clis/imooc/article.js +59 -0
- package/clis/imooc/login.js +1 -0
- package/clis/imooc/whoami.js +1 -0
- package/clis/indeed/job.js +20 -0
- package/clis/indeed/search.js +37 -0
- package/clis/indeed/utils.js +1 -0
- package/clis/infoq/article.js +19 -0
- package/clis/infoq/login.js +1 -0
- package/clis/infoq/whoami.js +1 -0
- package/clis/instagram/_shared/private-publish.js +75 -0
- package/clis/instagram/_shared/protocol-capture.js +180 -0
- package/clis/instagram/_shared/runtime-info.js +33 -0
- package/clis/instagram/auth.js +22 -0
- package/clis/instagram/collection-create.js +38 -0
- package/clis/instagram/collection-delete.js +72 -0
- package/clis/instagram/comment.js +26 -0
- package/clis/instagram/detail.js +2 -0
- package/clis/instagram/download.js +83 -0
- package/clis/instagram/explore.js +27 -0
- package/clis/instagram/follow.js +24 -0
- package/clis/instagram/followers.js +30 -0
- package/clis/instagram/following.js +74 -0
- package/clis/instagram/like.js +25 -0
- package/clis/instagram/note.js +162 -0
- package/clis/instagram/post.js +718 -0
- package/clis/instagram/profile.js +25 -0
- package/clis/instagram/reel.js +452 -0
- package/clis/instagram/save.js +25 -0
- package/clis/instagram/saved.js +37 -0
- package/clis/instagram/search.js +23 -0
- package/clis/instagram/story.js +21 -0
- package/clis/instagram/unfollow.js +21 -0
- package/clis/instagram/unlike.js +25 -0
- package/clis/instagram/unsave.js +25 -0
- package/clis/instagram/user.js +1 -0
- package/clis/jd/add-cart.js +22 -0
- package/clis/jd/auth.js +12 -0
- package/clis/jd/cart.js +59 -0
- package/clis/jd/detail.js +42 -0
- package/clis/jd/item.js +98 -0
- package/clis/jd/item.test.ts +517 -0
- package/clis/jd/reviews.js +31 -0
- package/clis/jd/search.js +42 -0
- package/clis/jianshu/article.js +1 -0
- package/clis/jianshu/login.js +1 -0
- package/clis/jianshu/notebooks.js +1 -0
- package/clis/jianshu/whoami.js +1 -0
- package/clis/jianyu/auth.js +28 -0
- package/clis/jianyu/detail.js +1 -0
- package/clis/jianyu/search.js +73 -0
- package/clis/jianyu/shared/china-bid-search.js +86 -0
- package/clis/jianyu/shared/procurement-contract.js +1 -0
- package/clis/jianyu/shared/procurement-detail.js +18 -0
- package/clis/jike/auth.js +19 -0
- package/clis/jike/comment.js +82 -0
- package/clis/jike/create.js +83 -0
- package/clis/jike/feed.js +32 -0
- package/clis/jike/like.js +38 -0
- package/clis/jike/notifications.js +115 -0
- package/clis/jike/post.js +33 -0
- package/clis/jike/repost.js +67 -0
- package/clis/jike/search.js +29 -0
- package/clis/jike/topic.js +20 -0
- package/clis/jike/user.js +19 -0
- package/clis/jike/utils.js +15 -0
- package/clis/jimeng/auth.js +13 -0
- package/clis/jimeng/generate.js +60 -0
- package/clis/jimeng/history.js +32 -0
- package/clis/jimeng/new.js +26 -0
- package/clis/jimeng/workspaces.js +22 -0
- package/clis/jira/attachments.js +1 -0
- package/clis/jira/comments.js +1 -0
- package/clis/jira/issue.js +1 -0
- package/clis/jira/links.js +1 -0
- package/clis/jira/search.js +1 -0
- package/clis/jira/shared.js +1 -0
- package/clis/juejin/article.js +5 -0
- package/clis/juejin/categories.js +1 -0
- package/clis/juejin/hot.js +1 -0
- package/clis/juejin/login.js +1 -0
- package/clis/juejin/recommend.js +1 -0
- package/clis/juejin/stats.js +53 -0
- package/clis/juejin/tags.js +1 -0
- package/clis/juejin/utils.js +1 -0
- package/clis/juejin/whoami.js +1 -0
- package/clis/ke/auth.js +14 -0
- package/clis/ke/chengjiao.js +45 -0
- package/clis/ke/ershoufang.js +50 -0
- package/clis/ke/utils.js +21 -0
- package/clis/ke/xiaoqu.js +45 -0
- package/clis/ke/zufang.js +50 -0
- package/clis/kimi/_utils.js +38 -0
- package/clis/kimi/audit-extras.js +64 -0
- package/clis/kimi/auth.js +17 -0
- package/clis/kimi/chat.js +77 -0
- package/clis/kimi/storage.js +14 -0
- package/clis/kimi/ui.js +89 -0
- package/clis/kimi/usage.js +51 -0
- package/clis/kuaishou/auth.js +1 -0
- package/clis/kuaishou/publish.js +53 -0
- package/clis/lesswrong/_helpers.js +9 -0
- package/clis/lesswrong/comments.js +9 -0
- package/clis/lesswrong/curated.js +5 -0
- package/clis/lesswrong/frontpage.js +5 -0
- package/clis/lesswrong/new.js +5 -0
- package/clis/lesswrong/read.js +5 -0
- package/clis/lesswrong/sequences.js +5 -0
- package/clis/lesswrong/shortform.js +5 -0
- package/clis/lesswrong/tag.js +5 -0
- package/clis/lesswrong/tags.js +5 -0
- package/clis/lesswrong/top-month.js +5 -0
- package/clis/lesswrong/top-week.js +5 -0
- package/clis/lesswrong/top-year.js +5 -0
- package/clis/lesswrong/top.js +5 -0
- package/clis/lesswrong/user-posts.js +5 -0
- package/clis/lesswrong/user.js +5 -0
- package/clis/lichess/top.js +1 -0
- package/clis/lichess/user.js +1 -0
- package/clis/lichess/utils.js +1 -0
- package/clis/linkedin/auth.js +27 -0
- package/clis/linkedin/comment.js +1 -0
- package/clis/linkedin/connect.js +244 -0
- package/clis/linkedin/create.js +113 -0
- package/clis/linkedin/follow.js +1 -0
- package/clis/linkedin/inbox.js +1 -0
- package/clis/linkedin/job-detail.js +40 -0
- package/clis/linkedin/jobs-preferences.js +34 -0
- package/clis/linkedin/like.js +1 -0
- package/clis/linkedin/notifications.js +1 -0
- package/clis/linkedin/people-search.js +1 -0
- package/clis/linkedin/post-analytics.js +1 -0
- package/clis/linkedin/post-comments.js +1 -0
- package/clis/linkedin/post-detail.js +1 -0
- package/clis/linkedin/posts-core.js +138 -0
- package/clis/linkedin/posts.js +1 -0
- package/clis/linkedin/profile-analytics.js +1 -0
- package/clis/linkedin/profile-api.js +218 -0
- package/clis/linkedin/profile-experience.js +1 -0
- package/clis/linkedin/profile-projects.js +1 -0
- package/clis/linkedin/profile-read.js +3 -0
- package/clis/linkedin/reply.js +1 -0
- package/clis/linkedin/repost.js +1 -0
- package/clis/linkedin/safe-send.js +155 -0
- package/clis/linkedin/salesnav-inbox.js +21 -0
- package/clis/linkedin/salesnav-message.js +33 -0
- package/clis/linkedin/salesnav-search.js +15 -0
- package/clis/linkedin/salesnav-thread.js +1 -0
- package/clis/linkedin/search-api.js +42 -0
- package/clis/linkedin/search.js +50 -0
- package/clis/linkedin/sent-invitations.js +46 -0
- package/clis/linkedin/services-read.js +57 -0
- package/clis/linkedin/shared.js +10 -0
- package/clis/linkedin/social-api.js +260 -0
- package/clis/linkedin/thread-snapshot.js +81 -0
- package/clis/linkedin/timeline.js +37 -0
- package/clis/linkedin-learning/auth.js +27 -0
- package/clis/linkedin-learning/course.js +17 -0
- package/clis/linkedin-learning/search.js +17 -0
- package/clis/linkedin-learning/trending.js +17 -0
- package/clis/linux-do/auth.js +20 -0
- package/clis/linux-do/categories.js +1 -0
- package/clis/linux-do/feed.js +19 -0
- package/clis/linux-do/search.js +1 -0
- package/clis/linux-do/tags.js +1 -0
- package/clis/linux-do/topic-content.js +28 -0
- package/clis/linux-do/topic.js +1 -0
- package/clis/linux-do/user-posts.js +1 -0
- package/clis/linux-do/user-topics.js +1 -0
- package/clis/lobsters/active.js +1 -0
- package/clis/lobsters/domain.js +1 -0
- package/clis/lobsters/hot.js +1 -0
- package/clis/lobsters/newest.js +1 -0
- package/clis/lobsters/read.js +11 -0
- package/clis/lobsters/tag.js +1 -0
- package/clis/maimai/auth.js +19 -0
- package/clis/maimai/search-talents.js +37 -0
- package/clis/manus/_utils.js +28 -0
- package/clis/manus/auth.js +20 -0
- package/clis/manus/connectors.js +4 -0
- package/clis/manus/credits.js +4 -0
- package/clis/manus/list.js +7 -0
- package/clis/manus/read.js +7 -0
- package/clis/manus/skills.js +4 -0
- package/clis/manus/status.js +8 -0
- package/clis/mastodon/post.js +1 -0
- package/clis/mastodon/shared.js +48 -0
- package/clis/mastodon/whoami.js +1 -0
- package/clis/maven/artifact.js +1 -0
- package/clis/maven/search.js +1 -0
- package/clis/maven/utils.js +1 -0
- package/clis/mdn/search.js +1 -0
- package/clis/medium/article.js +12 -0
- package/clis/medium/feed.js +1 -0
- package/clis/medium/login.js +1 -0
- package/clis/medium/search.js +1 -0
- package/clis/medium/stats.js +56 -0
- package/clis/medium/tag.js +1 -0
- package/clis/medium/user.js +1 -0
- package/clis/medium/utils.js +62 -0
- package/clis/medium/whoami.js +1 -0
- package/clis/mubu/doc.js +1 -0
- package/clis/mubu/docs.js +1 -0
- package/clis/mubu/notes.js +1 -0
- package/clis/mubu/recent.js +1 -0
- package/clis/mubu/search.js +1 -0
- package/clis/mubu/utils.js +31 -0
- package/clis/note/auth.js +12 -0
- package/clis/note/note-utils.js +2 -0
- package/clis/note/publish.js +17 -0
- package/clis/notebooklm/add-source.js +46 -0
- package/clis/notebooklm/auth.js +21 -0
- package/clis/notebooklm/create.js +1 -0
- package/clis/notebooklm/current.js +1 -0
- package/clis/notebooklm/generate-audio.js +1 -0
- package/clis/notebooklm/generate-slides.js +1 -0
- package/clis/notebooklm/get.js +1 -0
- package/clis/notebooklm/history.js +1 -0
- package/clis/notebooklm/list.js +1 -0
- package/clis/notebooklm/note-list.js +1 -0
- package/clis/notebooklm/notes-get.js +1 -0
- package/clis/notebooklm/open.js +1 -0
- package/clis/notebooklm/rpc.js +36 -0
- package/clis/notebooklm/shared.js +1 -0
- package/clis/notebooklm/source-fulltext.js +1 -0
- package/clis/notebooklm/source-get.js +1 -0
- package/clis/notebooklm/source-guide.js +1 -0
- package/clis/notebooklm/source-list.js +1 -0
- package/clis/notebooklm/status.js +1 -0
- package/clis/notebooklm/summary.js +1 -0
- package/clis/notebooklm/utils.js +172 -0
- package/clis/notebooklm/write-note.js +1 -0
- package/clis/nowcoder/auth.js +24 -0
- package/clis/nowcoder/comment.js +1 -0
- package/clis/nowcoder/comments.js +1 -0
- package/clis/nowcoder/companies.js +1 -0
- package/clis/nowcoder/creators.js +1 -0
- package/clis/nowcoder/delete.js +1 -0
- package/clis/nowcoder/detail.js +46 -0
- package/clis/nowcoder/dm-list.js +1 -0
- package/clis/nowcoder/dm-read.js +1 -0
- package/clis/nowcoder/dm-send.js +1 -0
- package/clis/nowcoder/drafts.js +1 -0
- package/clis/nowcoder/experience.js +20 -0
- package/clis/nowcoder/hot.js +1 -0
- package/clis/nowcoder/inbox.js +1 -0
- package/clis/nowcoder/jobs.js +1 -0
- package/clis/nowcoder/like.js +1 -0
- package/clis/nowcoder/my.js +1 -0
- package/clis/nowcoder/notifications.js +16 -0
- package/clis/nowcoder/papers.js +22 -0
- package/clis/nowcoder/post.js +1 -0
- package/clis/nowcoder/practice.js +21 -0
- package/clis/nowcoder/recommend.js +1 -0
- package/clis/nowcoder/referral.js +21 -0
- package/clis/nowcoder/salary.js +22 -0
- package/clis/nowcoder/search.js +31 -0
- package/clis/nowcoder/suggest.js +17 -0
- package/clis/nowcoder/topics.js +1 -0
- package/clis/nowcoder/trending.js +1 -0
- package/clis/nowcoder/utils.js +68 -0
- package/clis/npm/downloads.js +1 -0
- package/clis/npm/package.js +1 -0
- package/clis/npm/search.js +1 -0
- package/clis/npm/utils.js +1 -0
- package/clis/nuget/package.js +1 -0
- package/clis/nuget/search.js +1 -0
- package/clis/nuget/utils.js +1 -0
- package/clis/nvd/cve.js +1 -0
- package/clis/oeis/search.js +1 -0
- package/clis/oeis/sequence.js +1 -0
- package/clis/oeis/utils.js +1 -0
- package/clis/ones/common.js +23 -0
- package/clis/ones/enrich-tasks.js +1 -0
- package/clis/ones/login.js +2 -0
- package/clis/ones/logout.js +1 -0
- package/clis/ones/me.js +1 -0
- package/clis/ones/my-tasks.js +1 -0
- package/clis/ones/resolve-labels.js +1 -0
- package/clis/ones/task-helpers.js +1 -0
- package/clis/ones/task.js +1 -0
- package/clis/ones/tasks.js +1 -0
- package/clis/ones/token-info.js +1 -0
- package/clis/ones/worklog.js +14 -0
- package/clis/openalex/search.js +1 -0
- package/clis/openalex/utils.js +1 -0
- package/clis/openalex/work.js +1 -0
- package/clis/openfda/drug-label.js +1 -0
- package/clis/openfda/food-recall.js +1 -0
- package/clis/openfda/utils.js +1 -0
- package/clis/openreview/author.js +1 -0
- package/clis/openreview/paper.js +1 -0
- package/clis/openreview/reviews.js +4 -0
- package/clis/openreview/search.js +1 -0
- package/clis/openreview/utils.js +1 -0
- package/clis/openreview/venue.js +1 -0
- package/clis/oschina/article.js +1 -0
- package/clis/oschina/catalogs.js +1 -0
- package/clis/oschina/login.js +1 -0
- package/clis/oschina/stats.js +54 -0
- package/clis/oschina/whoami.js +1 -0
- package/clis/osv/query.js +1 -0
- package/clis/osv/utils.js +1 -0
- package/clis/osv/vulnerability.js +1 -0
- package/clis/packagist/package.js +1 -0
- package/clis/packagist/search.js +1 -0
- package/clis/packagist/utils.js +1 -0
- package/clis/paperreview/feedback.js +1 -0
- package/clis/paperreview/review.js +1 -0
- package/clis/paperreview/submit.js +1 -0
- package/clis/paperreview/utils.js +1 -0
- package/clis/pinterest/api.js +1 -0
- package/clis/pinterest/auth.js +1 -0
- package/clis/pinterest/boards.js +1 -0
- package/clis/pinterest/client.js +20 -0
- package/clis/pinterest/publish.js +13 -0
- package/clis/pixiv/auth.js +17 -0
- package/clis/pixiv/detail.js +1 -0
- package/clis/pixiv/download.js +1 -0
- package/clis/pixiv/illusts.js +1 -0
- package/clis/pixiv/ranking.js +26 -0
- package/clis/pixiv/search.js +1 -0
- package/clis/pixiv/user.js +1 -0
- package/clis/pixiv/utils.js +18 -0
- package/clis/powerchina/auth.js +17 -0
- package/clis/powerchina/search.js +1 -0
- package/clis/producthunt/browse.js +54 -0
- package/clis/producthunt/hot.js +64 -0
- package/clis/producthunt/posts.js +1 -0
- package/clis/producthunt/today.js +1 -0
- package/clis/producthunt/utils.js +1 -0
- package/clis/pubmed/article.js +1 -0
- package/clis/pubmed/author.js +1 -0
- package/clis/pubmed/citations.js +1 -0
- package/clis/pubmed/clinical-trial.js +1 -0
- package/clis/pubmed/journal.js +1 -0
- package/clis/pubmed/mesh.js +1 -0
- package/clis/pubmed/related.js +1 -0
- package/clis/pubmed/review.js +1 -0
- package/clis/pubmed/search.js +1 -0
- package/clis/pubmed/utils.js +1 -0
- package/clis/pypi/downloads.js +1 -0
- package/clis/pypi/package.js +1 -0
- package/clis/pypi/utils.js +1 -0
- package/clis/qiita/auth.js +1 -0
- package/clis/qiita/gql.js +32 -0
- package/clis/qiita/publish.js +7 -0
- package/clis/qiita/stats.js +37 -0
- package/clis/qoder/_utils.js +113 -0
- package/clis/qoder/composer.js +1 -0
- package/clis/qoder/history.js +25 -0
- package/clis/qoder/quest.js +1 -0
- package/clis/qoder/read.js +1 -0
- package/clis/qoder/status.js +1 -0
- package/clis/qoder/ui.js +59 -0
- package/clis/quark/auth.js +16 -0
- package/clis/quark/ls.js +1 -0
- package/clis/quark/mkdir.js +1 -0
- package/clis/quark/mv.js +1 -0
- package/clis/quark/rename.js +1 -0
- package/clis/quark/rm.js +1 -0
- package/clis/quark/save.js +1 -0
- package/clis/quark/share-tree.js +1 -0
- package/clis/quark/utils.js +13 -0
- package/clis/quora/create-answer.js +22 -0
- package/clis/quora/create-question.js +24 -0
- package/clis/quora/shared.js +43 -0
- package/clis/quora/whoami.js +1 -0
- package/clis/qwen/ask.js +1 -0
- package/clis/qwen/auth.js +17 -0
- package/clis/qwen/detail.js +1 -0
- package/clis/qwen/history.js +1 -0
- package/clis/qwen/image.js +31 -0
- package/clis/qwen/new.js +1 -0
- package/clis/qwen/read.js +1 -0
- package/clis/qwen/send.js +1 -0
- package/clis/qwen/status.js +1 -0
- package/clis/qwen/utils.js +185 -0
- package/clis/reddit/auth.js +17 -0
- package/clis/reddit/comment.js +36 -0
- package/clis/reddit/frontpage.js +40 -0
- package/clis/reddit/home.js +32 -0
- package/clis/reddit/hot.js +46 -0
- package/clis/reddit/popular.js +46 -0
- package/clis/reddit/read.js +398 -0
- package/clis/reddit/reply.js +56 -0
- package/clis/reddit/save.js +30 -0
- package/clis/reddit/saved.js +24 -0
- package/clis/reddit/search.js +51 -0
- package/clis/reddit/submit.js +42 -0
- package/clis/reddit/subreddit-info.js +29 -0
- package/clis/reddit/subreddit.js +52 -0
- package/clis/reddit/subscribe.js +29 -0
- package/clis/reddit/subscribed.js +68 -0
- package/clis/reddit/upvote.js +43 -0
- package/clis/reddit/upvoted.js +24 -0
- package/clis/reddit/user-comments.js +20 -0
- package/clis/reddit/user-posts.js +17 -0
- package/clis/reddit/user.js +20 -0
- package/clis/reddit/whoami.js +19 -0
- package/clis/rednote/auth.js +19 -0
- package/clis/rednote/comments.js +1 -0
- package/clis/rednote/download.js +1 -0
- package/clis/rednote/feed.js +1 -0
- package/clis/rednote/note.js +1 -0
- package/clis/rednote/notifications.js +67 -0
- package/clis/rednote/search.js +32 -0
- package/clis/rednote/user.js +1 -0
- package/clis/rest-countries/country.js +1 -0
- package/clis/rest-countries/region.js +1 -0
- package/clis/rest-countries/utils.js +1 -0
- package/clis/reuters/article-detail.js +1 -0
- package/clis/reuters/auth.js +39 -0
- package/clis/reuters/search.js +1 -0
- package/clis/reuters/utils.js +50 -0
- package/clis/rfc/rfc.js +1 -0
- package/clis/rfc/utils.js +1 -0
- package/clis/rubygems/gem.js +1 -0
- package/clis/rubygems/search.js +1 -0
- package/clis/rubygems/utils.js +1 -0
- package/clis/segmentfault/_signed-request.js +43 -0
- package/clis/segmentfault/article.js +3 -0
- package/clis/segmentfault/channels.js +1 -0
- package/clis/segmentfault/login.js +1 -0
- package/clis/segmentfault/stats.js +82 -0
- package/clis/segmentfault/tags.js +1 -0
- package/clis/segmentfault/whoami.js +1 -0
- package/clis/semanticscholar/citations.js +1 -0
- package/clis/semanticscholar/paper.js +1 -0
- package/clis/semanticscholar/recommendations.js +1 -0
- package/clis/semanticscholar/search.js +1 -0
- package/clis/semanticscholar/utils.js +1 -0
- package/clis/sinablog/article.js +1 -0
- package/clis/sinablog/hot.js +1 -0
- package/clis/sinablog/search.js +1 -0
- package/clis/sinablog/user.js +1 -0
- package/clis/sinablog/utils.js +153 -0
- package/clis/sinafinance/news.js +1 -0
- package/clis/sinafinance/rolling-news.js +20 -0
- package/clis/sinafinance/stock-rank.js +41 -0
- package/clis/sinafinance/stock.js +1 -0
- package/clis/slock/attachment-download.js +1 -0
- package/clis/slock/attachment-upload.js +34 -0
- package/clis/slock/attachment-url.js +1 -0
- package/clis/slock/auth-verify.js +1 -0
- package/clis/slock/bookmark-add.js +11 -0
- package/clis/slock/bookmark-list.js +10 -0
- package/clis/slock/bookmark-remove.js +7 -0
- package/clis/slock/channel-action.js +1 -0
- package/clis/slock/channel-archive.js +1 -0
- package/clis/slock/channel-create.js +1 -0
- package/clis/slock/channel-files.js +1 -0
- package/clis/slock/channel-info.js +1 -0
- package/clis/slock/channel-join.js +1 -0
- package/clis/slock/channel-leave.js +1 -0
- package/clis/slock/channel-list.js +1 -0
- package/clis/slock/channel-mark.js +1 -0
- package/clis/slock/channel-members.js +1 -0
- package/clis/slock/channel-unarchive.js +1 -0
- package/clis/slock/dm-list.js +1 -0
- package/clis/slock/errors.js +1 -0
- package/clis/slock/in-page.js +113 -0
- package/clis/slock/inbox-done.js +9 -0
- package/clis/slock/inbox-read-all.js +1 -0
- package/clis/slock/inbox.js +1 -0
- package/clis/slock/login.js +1 -0
- package/clis/slock/message-read.js +61 -0
- package/clis/slock/message-search.js +24 -0
- package/clis/slock/message-send.js +55 -0
- package/clis/slock/reaction-add.js +1 -0
- package/clis/slock/reaction-remove.js +1 -0
- package/clis/slock/resolve.js +1 -0
- package/clis/slock/server-list.js +9 -0
- package/clis/slock/server-use.js +18 -0
- package/clis/slock/shared.js +1 -0
- package/clis/slock/task-claim.js +14 -0
- package/clis/slock/task-convert.js +35 -0
- package/clis/slock/task-create.js +24 -0
- package/clis/slock/task-delete.js +9 -0
- package/clis/slock/task-get.js +11 -0
- package/clis/slock/task-list-server.js +12 -0
- package/clis/slock/task-list.js +14 -0
- package/clis/slock/task-status.js +19 -0
- package/clis/slock/task-unclaim.js +13 -0
- package/clis/slock/thread-done.js +1 -0
- package/clis/slock/thread-follow.js +1 -0
- package/clis/slock/thread-list.js +1 -0
- package/clis/slock/thread-state.js +1 -0
- package/clis/slock/thread-undone.js +1 -0
- package/clis/slock/thread-unfollow.js +1 -0
- package/clis/slock/unread-summary.js +18 -0
- package/clis/slock/whoami.js +1 -0
- package/clis/smzdm/search.js +71 -0
- package/clis/sohu/article.js +1 -0
- package/clis/sohu/attributes.js +1 -0
- package/clis/sohu/categories.js +1 -0
- package/clis/sohu/channels.js +1 -0
- package/clis/sohu/columns.js +1 -0
- package/clis/sohu/login.js +1 -0
- package/clis/sohu/mpbp.js +1 -0
- package/clis/sohu/stats.js +95 -0
- package/clis/sohu/topics.js +1 -0
- package/clis/sohu/whoami.js +1 -0
- package/clis/spotify/spotify.js +1 -0
- package/clis/spotify/utils.js +8 -0
- package/clis/stackoverflow/bounties.js +1 -0
- package/clis/stackoverflow/hot.js +1 -0
- package/clis/stackoverflow/read.js +13 -0
- package/clis/stackoverflow/related.js +1 -0
- package/clis/stackoverflow/search.js +1 -0
- package/clis/stackoverflow/tag.js +1 -0
- package/clis/stackoverflow/unanswered.js +1 -0
- package/clis/stackoverflow/user.js +1 -0
- package/clis/stackoverflow/utils.js +1 -0
- package/clis/steam/app.js +1 -0
- package/clis/steam/search.js +1 -0
- package/clis/steam/top-sellers.js +1 -0
- package/clis/steam/utils.js +1 -0
- package/clis/substack/auth.js +11 -0
- package/clis/substack/feed.js +1 -0
- package/clis/substack/post-utils.js +2 -0
- package/clis/substack/publication.js +1 -0
- package/clis/substack/publish.js +10 -0
- package/clis/substack/search.js +1 -0
- package/clis/substack/utils.js +108 -0
- package/clis/suno/auth.js +21 -0
- package/clis/suno/download.js +17 -0
- package/clis/suno/generate.js +4 -0
- package/clis/suno/list.js +14 -0
- package/clis/suno/status.js +1 -0
- package/clis/suno/utils.js +102 -0
- package/clis/taobao/add-cart.js +93 -0
- package/clis/taobao/auth.js +8 -0
- package/clis/taobao/cart.js +68 -0
- package/clis/taobao/detail.js +48 -0
- package/clis/taobao/reviews.js +52 -0
- package/clis/taobao/search.js +64 -0
- package/clis/tdx/hot-rank.js +25 -0
- package/clis/telegram/send.js +18 -0
- package/clis/telegram/shared.js +21 -0
- package/clis/telegram/whoami.js +1 -0
- package/clis/test-utils.js +1 -0
- package/clis/threads/post.js +152 -0
- package/clis/threads/shared.js +27 -0
- package/clis/threads/whoami.js +1 -0
- package/clis/ths/hot-rank.js +27 -0
- package/clis/tieba/hot.js +16 -0
- package/clis/tieba/posts.js +1 -0
- package/clis/tieba/read.js +84 -0
- package/clis/tieba/search.js +54 -0
- package/clis/tieba/utils.js +1 -0
- package/clis/tiktok/auth.js +30 -0
- package/clis/tiktok/comment.js +69 -0
- package/clis/tiktok/creator-videos.js +45 -0
- package/clis/tiktok/explore.js +76 -0
- package/clis/tiktok/follow.js +49 -0
- package/clis/tiktok/following.js +103 -0
- package/clis/tiktok/friends.js +79 -0
- package/clis/tiktok/like.js +19 -0
- package/clis/tiktok/live.js +79 -0
- package/clis/tiktok/notifications.js +77 -0
- package/clis/tiktok/profile.js +26 -0
- package/clis/tiktok/publish.js +82 -0
- package/clis/tiktok/save.js +15 -0
- package/clis/tiktok/search.js +24 -0
- package/clis/tiktok/unfollow.js +59 -0
- package/clis/tiktok/unlike.js +19 -0
- package/clis/tiktok/unsave.js +17 -0
- package/clis/tiktok/user.js +143 -0
- package/clis/tiktok/utils.js +272 -0
- package/clis/toutiao/article.js +1 -0
- package/clis/toutiao/articles.js +6 -0
- package/clis/toutiao/auth.js +50 -0
- package/clis/toutiao/hot.js +1 -0
- package/clis/toutiao/utils.js +2 -0
- package/clis/trae-cn/activity.js +1 -0
- package/clis/trae-cn/approve.js +1 -0
- package/clis/trae-cn/ask.js +1 -0
- package/clis/trae-cn/dump.js +1 -0
- package/clis/trae-cn/export.js +3 -0
- package/clis/trae-cn/model.js +1 -0
- package/clis/trae-cn/new.js +1 -0
- package/clis/trae-cn/read.js +1 -0
- package/clis/trae-cn/screenshot.js +1 -0
- package/clis/trae-cn/select-model.js +1 -0
- package/clis/trae-cn/send.js +1 -0
- package/clis/trae-cn/setup.js +1 -0
- package/clis/trae-cn/status.js +1 -0
- package/clis/trae-cn/targets.js +1 -0
- package/clis/trae-cn/utils.js +456 -0
- package/clis/trae-cn/watch.js +3 -0
- package/clis/trae-solo/_actions.js +41 -0
- package/clis/trae-solo/_fs.js +6 -0
- package/clis/trae-solo/_state.js +2 -0
- package/clis/trae-solo/automation.js +43 -0
- package/clis/trae-solo/history.js +15 -0
- package/clis/trae-solo/mode.js +31 -0
- package/clis/trae-solo/model.js +50 -0
- package/clis/trae-solo/renderer-storage.js +11 -0
- package/clis/trae-solo/settings.js +1 -0
- package/clis/trae-solo/skill-fs.js +1 -0
- package/clis/trae-solo/skill.js +81 -0
- package/clis/trae-solo/state-fs.js +2 -0
- package/clis/trae-solo/status.js +1 -0
- package/clis/trae-solo/task-fs.js +5 -0
- package/clis/trae-solo/user-rules.js +1 -0
- package/clis/trae-solo/workspaces-fs.js +1 -0
- package/clis/tumblr/auth.js +24 -0
- package/clis/tumblr/publish.js +14 -0
- package/clis/tvmaze/search.js +1 -0
- package/clis/tvmaze/show.js +1 -0
- package/clis/tvmaze/utils.js +1 -0
- package/clis/twitter/accept.js +116 -0
- package/clis/twitter/article.js +116 -0
- package/clis/twitter/auth.js +4 -0
- package/clis/twitter/block.js +62 -0
- package/clis/twitter/bookmark-folder.js +4 -0
- package/clis/twitter/bookmark-folders.js +4 -0
- package/clis/twitter/bookmark.js +44 -0
- package/clis/twitter/bookmarks.js +27 -0
- package/clis/twitter/delete.js +36 -0
- package/clis/twitter/device-follow.js +13 -0
- package/clis/twitter/dm-list.js +1 -0
- package/clis/twitter/dm-read.js +1 -0
- package/clis/twitter/dm-send.js +13 -0
- package/clis/twitter/dm-shared.js +6 -0
- package/clis/twitter/download.js +38 -0
- package/clis/twitter/follow-batch.js +43 -0
- package/clis/twitter/follow.js +39 -0
- package/clis/twitter/followers.js +59 -0
- package/clis/twitter/following.js +4 -0
- package/clis/twitter/hide-reply.js +57 -0
- package/clis/twitter/like.js +48 -0
- package/clis/twitter/likes.js +14 -0
- package/clis/twitter/list-add-batch.js +1 -0
- package/clis/twitter/list-add-core.js +35 -0
- package/clis/twitter/list-add.js +1 -0
- package/clis/twitter/list-batch-utils.js +1 -0
- package/clis/twitter/list-create.js +12 -0
- package/clis/twitter/list-delete.js +36 -0
- package/clis/twitter/list-remove-batch.js +1 -0
- package/clis/twitter/list-remove-core.js +127 -0
- package/clis/twitter/list-remove.js +1 -0
- package/clis/twitter/list-tweets.js +27 -0
- package/clis/twitter/lists.js +27 -0
- package/clis/twitter/notifications.js +4 -0
- package/clis/twitter/post.js +158 -0
- package/clis/twitter/profile.js +66 -0
- package/clis/twitter/quote.js +123 -0
- package/clis/twitter/reply-dm.js +112 -0
- package/clis/twitter/reply.js +97 -0
- package/clis/twitter/retweet.js +62 -0
- package/clis/twitter/schedule.js +96 -0
- package/clis/twitter/scheduled-delete.js +60 -0
- package/clis/twitter/scheduled-list.js +15 -0
- package/clis/twitter/scheduled-utils.js +18 -0
- package/clis/twitter/search.js +12 -0
- package/clis/twitter/shared.js +76 -0
- package/clis/twitter/thread.js +4 -0
- package/clis/twitter/timeline.js +4 -0
- package/clis/twitter/trending.js +18 -0
- package/clis/twitter/tweets.js +12 -0
- package/clis/twitter/unblock.js +45 -0
- package/clis/twitter/unbookmark.js +41 -0
- package/clis/twitter/unfollow.js +45 -0
- package/clis/twitter/unlike.js +48 -0
- package/clis/twitter/unretweet.js +62 -0
- package/clis/twitter/utils.js +55 -0
- package/clis/uisdc/news.js +25 -0
- package/clis/uiverse/_shared.js +257 -0
- package/clis/uiverse/code.js +1 -0
- package/clis/uiverse/preview.js +1 -0
- package/clis/upwork/auth.js +19 -0
- package/clis/upwork/detail.js +22 -0
- package/clis/upwork/feed.js +21 -0
- package/clis/upwork/search.js +21 -0
- package/clis/upwork/utils.js +1 -0
- package/clis/v2ex/auth.js +10 -0
- package/clis/v2ex/daily.js +41 -0
- package/clis/v2ex/hot.js +1 -0
- package/clis/v2ex/latest.js +1 -0
- package/clis/v2ex/me.js +58 -0
- package/clis/v2ex/member.js +1 -0
- package/clis/v2ex/node.js +1 -0
- package/clis/v2ex/nodes.js +1 -0
- package/clis/v2ex/notifications.js +40 -0
- package/clis/v2ex/replies.js +1 -0
- package/clis/v2ex/topic.js +1 -0
- package/clis/v2ex/user.js +1 -0
- package/clis/vk/post.js +1 -0
- package/clis/vk/shared.js +8 -0
- package/clis/vk/whoami.js +1 -0
- package/clis/wanfang/search.js +42 -0
- package/clis/web/read.js +260 -0
- package/clis/wechat-channels/auth.js +31 -0
- package/clis/wechat-channels/publish.js +298 -0
- package/clis/weibo/article.js +1 -0
- package/clis/weibo/auth.js +17 -0
- package/clis/weibo/comments.js +28 -0
- package/clis/weibo/delete.js +84 -0
- package/clis/weibo/favorites.js +27 -0
- package/clis/weibo/feed.js +33 -0
- package/clis/weibo/hot.js +17 -0
- package/clis/weibo/me.js +51 -0
- package/clis/weibo/post.js +46 -0
- package/clis/weibo/publish.js +98 -0
- package/clis/weibo/search.js +47 -0
- package/clis/weibo/user-posts.js +92 -0
- package/clis/weibo/user.js +37 -0
- package/clis/weibo/utils.js +16 -0
- package/clis/weixin/article.js +1 -0
- package/clis/weixin/create-draft.js +152 -0
- package/clis/weixin/download.js +100 -0
- package/clis/weixin/drafts.js +27 -0
- package/clis/weixin/login.js +1 -0
- package/clis/weixin/search.js +44 -0
- package/clis/weixin/stats.js +43 -0
- package/clis/weixin/whoami.js +1 -0
- package/clis/weread/ai-outline.js +2 -0
- package/clis/weread/auth.js +24 -0
- package/clis/weread/book-search.js +2 -0
- package/clis/weread/book.js +3 -0
- package/clis/weread/highlights.js +1 -0
- package/clis/weread/notebooks.js +1 -0
- package/clis/weread/notes.js +1 -0
- package/clis/weread/ranking.js +1 -0
- package/clis/weread/search.js +1 -0
- package/clis/weread/shelf.js +1 -0
- package/clis/weread/utils.js +67 -0
- package/clis/weread-official/book.js +1 -0
- package/clis/weread-official/discover.js +1 -0
- package/clis/weread-official/list-apis.js +1 -0
- package/clis/weread-official/notes.js +1 -0
- package/clis/weread-official/readdata.js +1 -0
- package/clis/weread-official/review.js +1 -0
- package/clis/weread-official/search.js +1 -0
- package/clis/weread-official/shelf.js +1 -0
- package/clis/weread-official/utils.js +1 -0
- package/clis/wikidata/entity.js +1 -0
- package/clis/wikidata/search.js +1 -0
- package/clis/wikidata/utils.js +1 -0
- package/clis/wikipedia/page.js +3 -0
- package/clis/wikipedia/random.js +1 -0
- package/clis/wikipedia/search.js +1 -0
- package/clis/wikipedia/summary.js +1 -0
- package/clis/wikipedia/trending.js +1 -0
- package/clis/wikipedia/utils.js +1 -0
- package/clis/wordpress/login.js +1 -0
- package/clis/wordpress/publish.js +1 -0
- package/clis/wordpress/shared.js +54 -0
- package/clis/wordpress/whoami.js +1 -0
- package/clis/woshipm/article.js +1 -0
- package/clis/woshipm/login.js +1 -0
- package/clis/woshipm/stats.js +52 -0
- package/clis/woshipm/whoami.js +1 -0
- package/clis/wttr/current.js +1 -0
- package/clis/wttr/forecast.js +1 -0
- package/clis/wttr/utils.js +1 -0
- package/clis/xianyu/auth.js +11 -0
- package/clis/xianyu/chat.js +1 -0
- package/clis/xianyu/im.js +240 -0
- package/clis/xianyu/inbox.js +1 -0
- package/clis/xianyu/item.js +97 -0
- package/clis/xianyu/messages.js +1 -0
- package/clis/xianyu/publish.js +245 -0
- package/clis/xianyu/reply.js +1 -0
- package/clis/xianyu/search.js +141 -0
- package/clis/xianyu/utils.js +1 -0
- package/clis/xiaoe/auth.js +10 -0
- package/clis/xiaoe/catalog.js +134 -0
- package/clis/xiaoe/content.js +16 -0
- package/clis/xiaoe/courses.js +43 -0
- package/clis/xiaoe/detail.js +19 -0
- package/clis/xiaoe/play-url.js +97 -0
- package/clis/xiaohongshu/ask.js +130 -0
- package/clis/xiaohongshu/auth.js +13 -0
- package/clis/xiaohongshu/collection-helpers.js +64 -0
- package/clis/xiaohongshu/comments.js +89 -0
- package/clis/xiaohongshu/creator-note-detail.js +80 -0
- package/clis/xiaohongshu/creator-notes-summary.js +1 -0
- package/clis/xiaohongshu/creator-notes.js +107 -0
- package/clis/xiaohongshu/creator-profile.js +13 -0
- package/clis/xiaohongshu/creator-stats.js +13 -0
- package/clis/xiaohongshu/delete-note.js +69 -0
- package/clis/xiaohongshu/download.js +200 -0
- package/clis/xiaohongshu/draft-clear.js +44 -0
- package/clis/xiaohongshu/draft-delete.js +37 -0
- package/clis/xiaohongshu/draft-open.js +1 -0
- package/clis/xiaohongshu/draft-utils.js +36 -0
- package/clis/xiaohongshu/drafts.js +1 -0
- package/clis/xiaohongshu/feed.js +38 -0
- package/clis/xiaohongshu/follow.js +75 -0
- package/clis/xiaohongshu/liked.js +1 -0
- package/clis/xiaohongshu/note-helpers.js +1 -0
- package/clis/xiaohongshu/note.js +32 -0
- package/clis/xiaohongshu/notifications.js +1 -0
- package/clis/xiaohongshu/publish-video.js +87 -0
- package/clis/xiaohongshu/publish.js +637 -0
- package/clis/xiaohongshu/saved.js +1 -0
- package/clis/xiaohongshu/search.js +194 -0
- package/clis/xiaohongshu/unfollow.js +98 -0
- package/clis/xiaohongshu/user-helpers.js +1 -0
- package/clis/xiaohongshu/user.js +30 -0
- package/clis/xiaoyuzhou/auth.js +3 -0
- package/clis/xiaoyuzhou/download.js +1 -0
- package/clis/xiaoyuzhou/episode.js +1 -0
- package/clis/xiaoyuzhou/podcast-episodes.js +1 -0
- package/clis/xiaoyuzhou/podcast.js +1 -0
- package/clis/xiaoyuzhou/transcript.js +1 -0
- package/clis/xiaoyuzhou/utils.js +1 -0
- package/clis/xueqiu/article.js +4 -0
- package/clis/xueqiu/auth.js +27 -0
- package/clis/xueqiu/comments.js +38 -0
- package/clis/xueqiu/danjuan-utils.js +84 -0
- package/clis/xueqiu/earnings-date.js +1 -0
- package/clis/xueqiu/events.js +17 -0
- package/clis/xueqiu/feed.js +1 -0
- package/clis/xueqiu/fund-holdings.js +1 -0
- package/clis/xueqiu/fund-snapshot.js +1 -0
- package/clis/xueqiu/groups.js +1 -0
- package/clis/xueqiu/hashtags.js +26 -0
- package/clis/xueqiu/hot-stock.js +1 -0
- package/clis/xueqiu/hot.js +1 -0
- package/clis/xueqiu/kline.js +1 -0
- package/clis/xueqiu/search.js +1 -0
- package/clis/xueqiu/stock.js +1 -0
- package/clis/xueqiu/stocks.js +13 -0
- package/clis/xueqiu/users.js +12 -0
- package/clis/xueqiu/utils.js +9 -0
- package/clis/xueqiu/watchlist.js +1 -0
- package/clis/yahoo/search.js +21 -0
- package/clis/yahoo-finance/quote.js +50 -0
- package/clis/yollomi/background.js +1 -0
- package/clis/yollomi/edit.js +1 -0
- package/clis/yollomi/face-swap.js +1 -0
- package/clis/yollomi/generate.js +1 -0
- package/clis/yollomi/models.js +1 -0
- package/clis/yollomi/object-remover.js +1 -0
- package/clis/yollomi/remove-bg.js +1 -0
- package/clis/yollomi/restore.js +1 -0
- package/clis/yollomi/try-on.js +1 -0
- package/clis/yollomi/upload.js +17 -0
- package/clis/yollomi/upscale.js +1 -0
- package/clis/yollomi/utils.js +16 -0
- package/clis/yollomi/video.js +1 -0
- package/clis/youdao/note.js +113 -0
- package/clis/youtube/auth.js +20 -0
- package/clis/youtube/channel.js +118 -0
- package/clis/youtube/comments.js +64 -0
- package/clis/youtube/feed.js +89 -0
- package/clis/youtube/history.js +87 -0
- package/clis/youtube/like.js +33 -0
- package/clis/youtube/playlist.js +47 -0
- package/clis/youtube/publish.js +56 -0
- package/clis/youtube/search.js +39 -0
- package/clis/youtube/subscribe.js +42 -0
- package/clis/youtube/subscriptions.js +26 -0
- package/clis/youtube/transcript-group.js +2 -0
- package/clis/youtube/transcript.js +417 -0
- package/clis/youtube/unlike.js +33 -0
- package/clis/youtube/unsubscribe.js +42 -0
- package/clis/youtube/utils.js +34 -0
- package/clis/youtube/video.js +96 -0
- package/clis/youtube/watch-later.js +41 -0
- package/clis/yuanbao/ask.js +145 -0
- package/clis/yuanbao/auth.js +23 -0
- package/clis/yuanbao/detail.js +1 -0
- package/clis/yuanbao/history.js +1 -0
- package/clis/yuanbao/new.js +16 -0
- package/clis/yuanbao/read.js +1 -0
- package/clis/yuanbao/send.js +1 -0
- package/clis/yuanbao/shared.js +127 -0
- package/clis/yuanbao/status.js +1 -0
- package/clis/yuque/article.js +1 -0
- package/clis/yuque/whoami.js +1 -0
- package/clis/zhihu/answer-comments.js +11 -0
- package/clis/zhihu/answer-detail.js +11 -0
- package/clis/zhihu/answer.js +15 -0
- package/clis/zhihu/article-transform.js +1 -0
- package/clis/zhihu/article.js +7 -0
- package/clis/zhihu/auth.js +11 -0
- package/clis/zhihu/collection.js +7 -0
- package/clis/zhihu/collections.js +7 -0
- package/clis/zhihu/comment.js +17 -0
- package/clis/zhihu/download.js +39 -0
- package/clis/zhihu/favorite.js +39 -0
- package/clis/zhihu/follow.js +19 -0
- package/clis/zhihu/followers.js +1 -0
- package/clis/zhihu/following.js +1 -0
- package/clis/zhihu/hot.js +21 -0
- package/clis/zhihu/like.js +16 -0
- package/clis/zhihu/paginate.js +11 -0
- package/clis/zhihu/pin.js +24 -0
- package/clis/zhihu/pins.js +1 -0
- package/clis/zhihu/question.js +7 -0
- package/clis/zhihu/recommend.js +7 -0
- package/clis/zhihu/search.js +11 -0
- package/clis/zhihu/stats.js +41 -0
- package/clis/zhihu/target.js +1 -0
- package/clis/zhihu/text.js +6 -0
- package/clis/zhihu/user-answers.js +1 -0
- package/clis/zhihu/user-arg.js +1 -0
- package/clis/zhihu/user-articles.js +1 -0
- package/clis/zhihu/user.js +11 -0
- package/clis/zhihu/write-shared.js +71 -0
- package/clis/zlibrary/info.js +1 -0
- package/clis/zlibrary/search.js +1 -0
- package/clis/zlibrary/utils.js +48 -0
- package/clis/zsxq/auth.js +33 -0
- package/clis/zsxq/dynamics.js +1 -0
- package/clis/zsxq/groups.js +1 -0
- package/clis/zsxq/search.js +1 -0
- package/clis/zsxq/topic.js +1 -0
- package/clis/zsxq/topics.js +1 -0
- package/clis/zsxq/utils.js +72 -0
- package/dist/src/adapter-shadow.d.ts +11 -0
- package/dist/src/adapter-shadow.js +2 -0
- package/dist/src/adapter-shadow.test.d.ts +1 -0
- package/dist/src/adapter-source.d.ts +11 -0
- package/dist/src/adapter-source.js +1 -0
- package/dist/src/adapter-source.test.d.ts +1 -0
- package/dist/src/browser/analyze.d.ts +118 -0
- package/dist/src/browser/analyze.js +1 -0
- package/dist/src/browser/analyze.test.d.ts +1 -0
- package/dist/src/browser/article-extract.d.ts +57 -0
- package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
- package/dist/src/browser/article-extract.js +2 -0
- package/dist/src/browser/article-extract.test.d.ts +1 -0
- package/dist/src/browser/ax-snapshot.d.ts +37 -0
- package/dist/src/browser/ax-snapshot.js +2 -0
- package/dist/src/browser/ax-snapshot.test.d.ts +1 -0
- package/dist/src/browser/base-page.d.ts +151 -0
- package/dist/src/browser/base-page.js +225 -0
- package/dist/src/browser/base-page.test.d.ts +1 -0
- package/dist/src/browser/bridge-readiness.d.ts +14 -0
- package/dist/src/browser/bridge-readiness.js +1 -0
- package/dist/src/browser/bridge-readiness.test.d.ts +1 -0
- package/dist/src/browser/bridge.d.ts +26 -0
- package/dist/src/browser/bridge.js +1 -0
- package/dist/src/browser/cdp-click-fixture.test.d.ts +1 -0
- package/dist/src/browser/cdp.d.ts +51 -0
- package/dist/src/browser/cdp.js +1 -0
- package/dist/src/browser/cdp.test.d.ts +1 -0
- package/dist/src/browser/compound.d.ts +59 -0
- package/dist/src/browser/compound.js +82 -0
- package/dist/src/browser/compound.test.d.ts +1 -0
- package/dist/src/browser/config.d.ts +2 -0
- package/dist/src/browser/config.js +1 -0
- package/dist/src/browser/daemon-client.d.ts +79 -0
- package/dist/src/browser/daemon-client.js +1 -0
- package/dist/src/browser/daemon-client.test.d.ts +1 -0
- package/dist/src/browser/daemon-lifecycle.d.ts +37 -0
- package/dist/src/browser/daemon-lifecycle.js +12 -0
- package/dist/src/browser/daemon-transport.d.ts +55 -0
- package/dist/src/browser/daemon-transport.js +1 -0
- package/dist/src/browser/daemon-version.d.ts +4 -0
- package/dist/src/browser/daemon-version.js +2 -0
- package/dist/src/browser/dom-helpers.d.ts +41 -0
- package/dist/src/browser/dom-helpers.js +166 -0
- package/dist/src/browser/dom-helpers.test.d.ts +1 -0
- package/dist/src/browser/dom-snapshot.d.ts +93 -0
- package/dist/src/browser/dom-snapshot.js +809 -0
- package/dist/src/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/src/browser/errors.d.ts +38 -0
- package/dist/src/browser/errors.js +5 -0
- package/dist/src/browser/errors.test.d.ts +1 -0
- package/dist/src/browser/extract.d.ts +69 -0
- package/dist/src/browser/extract.js +42 -0
- package/dist/src/browser/extract.test.d.ts +1 -0
- package/dist/src/browser/find.d.ts +84 -0
- package/dist/src/browser/find.js +328 -0
- package/dist/src/browser/find.test.d.ts +1 -0
- package/dist/src/browser/fingerprint.d.ts +44 -0
- package/dist/src/browser/fingerprint.js +82 -0
- package/dist/src/browser/html-tree.d.ts +75 -0
- package/dist/src/browser/html-tree.js +61 -0
- package/dist/src/browser/html-tree.test.d.ts +1 -0
- package/dist/src/browser/index.d.ts +14 -0
- package/dist/src/browser/index.js +1 -0
- package/dist/src/browser/managed-chrome.d.ts +89 -0
- package/dist/src/browser/managed-chrome.js +1 -0
- package/dist/src/browser/network-cache.d.ts +49 -0
- package/dist/src/browser/network-cache.js +1 -0
- package/dist/src/browser/network-cache.test.d.ts +1 -0
- package/dist/src/browser/network-key.d.ts +22 -0
- package/dist/src/browser/network-key.js +1 -0
- package/dist/src/browser/network-key.test.d.ts +1 -0
- package/dist/src/browser/page.d.ts +83 -0
- package/dist/src/browser/page.js +15 -0
- package/dist/src/browser/page.test.d.ts +1 -0
- package/dist/src/browser/profile.d.ts +96 -0
- package/dist/src/browser/profile.js +2 -0
- package/dist/src/browser/proxy-test.d.ts +28 -0
- package/dist/src/browser/proxy-test.js +12 -0
- package/dist/src/browser/shape-filter.d.ts +52 -0
- package/dist/src/browser/shape-filter.js +1 -0
- package/dist/src/browser/shape-filter.test.d.ts +1 -0
- package/dist/src/browser/shape.d.ts +23 -0
- package/dist/src/browser/shape.js +1 -0
- package/dist/src/browser/shape.test.d.ts +1 -0
- package/dist/src/browser/stealth.d.ts +18 -0
- package/dist/src/browser/stealth.js +391 -0
- package/dist/src/browser/stealth.test.d.ts +1 -0
- package/dist/src/browser/tabs.d.ts +13 -0
- package/dist/src/browser/tabs.js +2 -0
- package/dist/src/browser/target-errors.d.ts +39 -0
- package/dist/src/browser/target-errors.js +1 -0
- package/dist/src/browser/target-errors.test.d.ts +1 -0
- package/dist/src/browser/target-resolver.d.ts +158 -0
- package/dist/src/browser/target-resolver.js +423 -0
- package/dist/src/browser/target-resolver.test.d.ts +1 -0
- package/dist/src/browser/trace.d.ts +21 -0
- package/dist/src/browser/trace.js +2 -0
- package/dist/src/browser/utils.d.ts +20 -0
- package/dist/src/browser/utils.js +1 -0
- package/dist/src/browser/utils.test.d.ts +1 -0
- package/dist/src/browser/verify-fixture.d.ts +65 -0
- package/dist/src/browser/verify-fixture.js +2 -0
- package/dist/src/browser/verify-fixture.test.d.ts +1 -0
- package/dist/src/browser/visual-refs.d.ts +11 -0
- package/dist/src/browser/visual-refs.js +94 -0
- package/dist/src/browser-session-lock.d.ts +13 -0
- package/dist/src/browser-session-lock.js +1 -0
- package/dist/src/browser-session-lock.test.d.ts +1 -0
- package/dist/src/browser.test.d.ts +1 -0
- package/dist/src/build-manifest.d.ts +103 -0
- package/dist/src/build-manifest.js +16 -0
- package/dist/src/build-manifest.test.d.ts +1 -0
- package/dist/src/capabilityRouting.d.ts +19 -0
- package/dist/src/capabilityRouting.js +1 -0
- package/dist/src/capabilityRouting.test.d.ts +1 -0
- package/dist/src/cli-argv-preprocess.d.ts +63 -0
- package/dist/src/cli-argv-preprocess.js +1 -0
- package/dist/src/cli-argv-preprocess.test.d.ts +1 -0
- package/dist/src/cli.d.ts +79 -0
- package/dist/src/cli.js +132 -0
- package/dist/src/cli.test.d.ts +1 -0
- package/dist/src/commanderAdapter.d.ts +21 -0
- package/dist/src/commanderAdapter.js +3 -0
- package/dist/src/commanderAdapter.test.d.ts +1 -0
- package/dist/src/commands/auth.d.ts +40 -0
- package/dist/src/commands/auth.js +2 -0
- package/dist/src/commands/auth.test.d.ts +1 -0
- package/dist/src/commands/daemon.d.ts +25 -0
- package/dist/src/commands/daemon.js +1 -0
- package/dist/src/commands/daemon.test.d.ts +1 -0
- package/dist/src/completion-fast.d.ts +22 -0
- package/dist/src/completion-fast.js +1 -0
- package/dist/src/completion-shared.d.ts +13 -0
- package/dist/src/completion-shared.js +30 -0
- package/dist/src/completion.d.ts +21 -0
- package/dist/src/completion.js +1 -0
- package/dist/src/completion.test.d.ts +1 -0
- package/dist/src/constants.d.ts +22 -0
- package/dist/src/constants.js +1 -0
- package/dist/src/convention-audit.d.ts +50 -0
- package/dist/src/convention-audit.js +2 -0
- package/dist/src/convention-audit.test.d.ts +1 -0
- package/dist/src/daemon-utils.d.ts +18 -0
- package/dist/src/daemon-utils.js +1 -0
- package/dist/src/daemon.d.ts +22 -0
- package/dist/src/daemon.js +1 -0
- package/dist/src/daemon.test.d.ts +1 -0
- package/dist/src/discovery.d.ts +43 -0
- package/dist/src/discovery.js +2 -0
- package/dist/src/doctor.d.ts +38 -0
- package/dist/src/doctor.js +13 -0
- package/dist/src/doctor.test.d.ts +1 -0
- package/dist/src/download/article-download.d.ts +72 -0
- package/dist/src/download/article-download.js +21 -0
- package/dist/src/download/article-download.test.d.ts +1 -0
- package/dist/src/download/index.d.ts +71 -0
- package/dist/src/download/index.js +7 -0
- package/dist/src/download/index.test.d.ts +1 -0
- package/dist/src/download/media-download.d.ts +49 -0
- package/dist/src/download/media-download.js +1 -0
- package/dist/src/download/media-download.test.d.ts +1 -0
- package/dist/src/download/progress.d.ts +36 -0
- package/dist/src/download/progress.js +5 -0
- package/dist/src/download/progress.test.d.ts +1 -0
- package/dist/src/electron-apps.d.ts +31 -0
- package/dist/src/electron-apps.js +1 -0
- package/dist/src/electron-apps.test.d.ts +1 -0
- package/dist/src/engine.test.d.ts +1 -0
- package/dist/src/errors.d.ts +117 -0
- package/dist/src/errors.js +2 -0
- package/dist/src/errors.test.d.ts +1 -0
- package/dist/src/execution.d.ts +24 -0
- package/dist/src/execution.js +2 -0
- package/dist/src/execution.test.d.ts +1 -0
- package/dist/src/extension-manifest-regression.test.d.ts +1 -0
- package/dist/src/external-clis.yaml +109 -0
- package/dist/src/external.d.ts +49 -0
- package/dist/src/external.js +1 -0
- package/dist/src/external.test.d.ts +1 -0
- package/dist/src/help.d.ts +88 -0
- package/dist/src/help.js +8 -0
- package/dist/src/help.test.d.ts +1 -0
- package/dist/src/hooks.d.ts +46 -0
- package/dist/src/hooks.js +1 -0
- package/dist/src/hooks.test.d.ts +4 -0
- package/dist/src/interceptor.d.ts +44 -0
- package/dist/src/interceptor.js +123 -0
- package/dist/src/interceptor.test.d.ts +4 -0
- package/dist/src/launcher.d.ts +41 -0
- package/dist/src/launcher.js +7 -0
- package/dist/src/launcher.test.d.ts +1 -0
- package/dist/src/logger.d.ts +26 -0
- package/dist/src/logger.js +9 -0
- package/dist/src/main.d.ts +5 -0
- package/dist/src/main.js +10 -0
- package/dist/src/manifest-types.d.ts +43 -0
- package/dist/src/manifest-types.js +1 -0
- package/dist/src/node-network.d.ts +10 -0
- package/dist/src/node-network.js +1 -0
- package/dist/src/node-network.test.d.ts +1 -0
- package/dist/src/observation/artifact.d.ts +16 -0
- package/dist/src/observation/artifact.js +8 -0
- package/dist/src/observation/artifact.test.d.ts +1 -0
- package/dist/src/observation/events.d.ts +89 -0
- package/dist/src/observation/events.js +1 -0
- package/dist/src/observation/index.d.ts +7 -0
- package/dist/src/observation/index.js +1 -0
- package/dist/src/observation/manager.d.ts +9 -0
- package/dist/src/observation/manager.js +1 -0
- package/dist/src/observation/manager.test.d.ts +1 -0
- package/dist/src/observation/redaction.d.ts +11 -0
- package/dist/src/observation/redaction.js +2 -0
- package/dist/src/observation/redaction.test.d.ts +1 -0
- package/dist/src/observation/retention.d.ts +32 -0
- package/dist/src/observation/retention.js +1 -0
- package/dist/src/observation/retention.test.d.ts +1 -0
- package/dist/src/observation/ring-buffer.d.ts +22 -0
- package/dist/src/observation/ring-buffer.js +1 -0
- package/dist/src/observation/ring-buffer.test.d.ts +1 -0
- package/dist/src/observation/session.d.ts +25 -0
- package/dist/src/observation/session.js +1 -0
- package/dist/src/output.d.ts +14 -0
- package/dist/src/output.js +2 -0
- package/dist/src/output.test.d.ts +1 -0
- package/dist/src/package-exports.test.d.ts +1 -0
- package/dist/src/package-paths.d.ts +8 -0
- package/dist/src/package-paths.js +1 -0
- package/dist/src/pipeline/executor.d.ts +11 -0
- package/dist/src/pipeline/executor.js +1 -0
- package/dist/src/pipeline/executor.test.d.ts +4 -0
- package/dist/src/pipeline/index.d.ts +5 -0
- package/dist/src/pipeline/index.js +1 -0
- package/dist/src/pipeline/registry.d.ts +28 -0
- package/dist/src/pipeline/registry.js +1 -0
- package/dist/src/pipeline/steps/browser.d.ts +13 -0
- package/dist/src/pipeline/steps/browser.js +1 -0
- package/dist/src/pipeline/steps/download.d.ts +18 -0
- package/dist/src/pipeline/steps/download.js +1 -0
- package/dist/src/pipeline/steps/download.test.d.ts +1 -0
- package/dist/src/pipeline/steps/fetch.d.ts +5 -0
- package/dist/src/pipeline/steps/fetch.js +31 -0
- package/dist/src/pipeline/steps/fetch.test.d.ts +1 -0
- package/dist/src/pipeline/steps/intercept.d.ts +5 -0
- package/dist/src/pipeline/steps/intercept.js +1 -0
- package/dist/src/pipeline/steps/tap.d.ts +12 -0
- package/dist/src/pipeline/steps/tap.js +53 -0
- package/dist/src/pipeline/steps/transform.d.ts +9 -0
- package/dist/src/pipeline/steps/transform.js +1 -0
- package/dist/src/pipeline/template.d.ts +17 -0
- package/dist/src/pipeline/template.js +1 -0
- package/dist/src/pipeline/template.test.d.ts +4 -0
- package/dist/src/pipeline/transform.test.d.ts +4 -0
- package/dist/src/plugin-manifest.d.ts +70 -0
- package/dist/src/plugin-manifest.js +1 -0
- package/dist/src/plugin-manifest.test.d.ts +4 -0
- package/dist/src/plugin-scaffold.d.ts +28 -0
- package/dist/src/plugin-scaffold.js +75 -0
- package/dist/src/plugin-scaffold.test.d.ts +4 -0
- package/dist/src/plugin.d.ts +145 -0
- package/dist/src/plugin.js +20 -0
- package/dist/src/plugin.test.d.ts +4 -0
- package/dist/src/rate-limit.d.ts +15 -0
- package/dist/src/rate-limit.js +2 -0
- package/dist/src/rate-limit.test.d.ts +1 -0
- package/dist/src/registry-api.d.ts +13 -0
- package/dist/src/registry-api.js +1 -0
- package/dist/src/registry.d.ts +130 -0
- package/dist/src/registry.js +1 -0
- package/dist/src/registry.test.d.ts +4 -0
- package/dist/src/runtime-detect.d.ts +31 -0
- package/dist/src/runtime-detect.js +1 -0
- package/dist/src/runtime-detect.test.d.ts +1 -0
- package/dist/src/runtime.d.ts +57 -0
- package/dist/src/runtime.js +1 -0
- package/dist/src/scripts/framework.d.ts +7 -0
- package/dist/src/scripts/framework.js +1 -0
- package/dist/src/scripts/interact.d.ts +4 -0
- package/dist/src/scripts/interact.js +1 -0
- package/dist/src/scripts/store.d.ts +13 -0
- package/dist/src/scripts/store.js +1 -0
- package/dist/src/serialization.d.ts +42 -0
- package/dist/src/serialization.js +4 -0
- package/dist/src/serialization.test.d.ts +1 -0
- package/dist/src/skills.d.ts +14 -0
- package/dist/src/skills.js +6 -0
- package/dist/src/skills.test.d.ts +1 -0
- package/dist/src/snapshotFormatter.d.ts +11 -0
- package/dist/src/snapshotFormatter.js +3 -0
- package/dist/src/snapshotFormatter.test.d.ts +7 -0
- package/dist/src/tui.d.ts +28 -0
- package/dist/src/tui.js +16 -0
- package/dist/src/tui.test.d.ts +1 -0
- package/dist/src/types.d.ts +236 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils.d.ts +57 -0
- package/dist/src/utils.js +38 -0
- package/dist/src/utils.test.d.ts +1 -0
- package/dist/src/validate.d.ts +21 -0
- package/dist/src/validate.js +3 -0
- package/dist/src/validate.test.d.ts +9 -0
- package/dist/src/verify.d.ts +27 -0
- package/dist/src/verify.js +2 -0
- package/dist/src/verify.test.d.ts +8 -0
- package/dist/src/version.d.ts +4 -0
- package/dist/src/version.js +1 -0
- package/dist/src/weixin-download.test.d.ts +1 -0
- package/docs/guide/browser-bridge.md +94 -0
- package/docs/guide/electron-app-cli.md +199 -0
- package/docs/guide/exit-codes.md +24 -0
- package/docs/guide/extending-opencli.md +134 -0
- package/docs/guide/getting-started.md +81 -0
- package/docs/guide/installation.md +52 -0
- package/docs/guide/plugins.md +226 -0
- package/docs/guide/remote-orchestration.md +116 -0
- package/docs/guide/troubleshooting.md +69 -0
- package/package.json +116 -0
- package/scripts/check-doc-coverage.sh +81 -0
- package/scripts/check-listing-id-pairing.mjs +193 -0
- package/scripts/check-silent-column-drop.mjs +105 -0
- package/scripts/check-typed-error-lint.mjs +118 -0
- package/scripts/clean-dist.cjs +13 -0
- package/scripts/copy-yaml.cjs +12 -0
- package/scripts/fetch-adapters.js +293 -0
- package/scripts/minify-release.mjs +115 -0
- package/scripts/postinstall.js +174 -0
- package/scripts/silent-column-drop-baseline.json +910 -0
- package/scripts/typed-error-lint-baseline.json +1098 -0
- package/skills/opencli-adapter-author/SKILL.md +256 -0
- package/skills/opencli-adapter-author/references/adapter-template.md +471 -0
- package/skills/opencli-adapter-author/references/api-discovery.md +295 -0
- package/skills/opencli-adapter-author/references/coverage-matrix.md +81 -0
- package/skills/opencli-adapter-author/references/field-conventions.md +172 -0
- package/skills/opencli-adapter-author/references/field-decode-playbook.md +181 -0
- package/skills/opencli-adapter-author/references/jsdom-fixture-pattern.md +196 -0
- package/skills/opencli-adapter-author/references/output-design.md +149 -0
- package/skills/opencli-adapter-author/references/site-memory/bilibili.md +70 -0
- package/skills/opencli-adapter-author/references/site-memory/eastmoney.md +70 -0
- package/skills/opencli-adapter-author/references/site-memory/tonghuashun.md +51 -0
- package/skills/opencli-adapter-author/references/site-memory/xueqiu.md +58 -0
- package/skills/opencli-adapter-author/references/site-memory.md +231 -0
- package/skills/opencli-adapter-author/references/site-recon.md +134 -0
- package/skills/opencli-adapter-author/references/strategy-selection.md +169 -0
- package/skills/opencli-adapter-author/references/success-rate-pitfalls.md +162 -0
- package/skills/opencli-adapter-author/references/typed-errors.md +234 -0
- package/skills/opencli-autofix/SKILL.md +297 -0
- package/skills/opencli-browser/SKILL.md +468 -0
- package/skills/opencli-browser-sitemap/SKILL.md +96 -0
- package/skills/opencli-sitemap-author/SKILL.md +161 -0
- package/skills/opencli-sitemap-author/references/sitemap-schema.md +660 -0
- package/skills/opencli-usage/SKILL.md +168 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{cli as U,Strategy as P}from"@jackwener/opencli/registry";import{ArgumentError as S,AuthRequiredError as f,CommandExecutionError as O}from"@jackwener/opencli/errors";import{TWITTER_BEARER_TOKEN as C,applyTopByEngagement as F}from"./utils.js";import{extractMedia as N,resolveTwitterQueryId as _,describeTwitterApiError as R}from"./shared.js";const j="BookmarkFolderTimeline",x="13H7EUATwethsj_jZ6QQAQ",q=/^[A-Za-z0-9_-]+$/,k=100,p={rweb_video_screen_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!1,rweb_tipjar_consumption_enabled:!1,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,content_disclosure_indicator_enabled:!0,content_disclosure_ai_generated_indicator_enabled:!0,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!1,responsive_web_enhance_cards_enabled:!1};function L(J,V,z,H){const $={bookmark_collection_id:String(V),count:z,includePromotedContent:!1};if(H)$.cursor=H;return`/i/api/graphql/${J}/${j}?variables=${encodeURIComponent(JSON.stringify($))}&features=${encodeURIComponent(JSON.stringify(p))}`}export function extractFolderTweet(J,V){if(!J)return null;const z=J.tweet||J,H=z.legacy||{};if(!z.rest_id||V.has(z.rest_id))return null;V.add(z.rest_id);const $=z.core?.user_results?.result,W=$?.legacy?.screen_name||$?.core?.screen_name||"",Y=z.note_tweet?.note_tweet_results?.result?.text;return{id:z.rest_id,author:W,text:Y||H.full_text||"",likes:H.favorite_count||0,retweets:H.retweet_count||0,bookmarks:H.bookmark_count||0,created_at:H.created_at||"",url:W?`https://x.com/${W}/status/${z.rest_id}`:`https://x.com/i/status/${z.rest_id}`,...N(H)}}export function parseBookmarkFolderTimeline(J,V){const z=[];let H=null;const $=J?.data?.bookmark_collection_timeline?.timeline?.instructions||J?.data?.bookmark_timeline_v2?.timeline?.instructions||J?.data?.bookmark_timeline?.timeline?.instructions||[];for(const W of $)for(const Y of W.entries||[]){const Z=Y.content;if(Z?.entryType==="TimelineTimelineCursor"||Z?.__typename==="TimelineTimelineCursor"){if(Z.cursorType==="Bottom"||Z.cursorType==="ShowMore")H=Z.value;continue}if(Y.entryId?.startsWith("cursor-bottom-")||Y.entryId?.startsWith("cursor-showMore-")){H=Z?.value||Z?.itemContent?.value||H;continue}const X=extractFolderTweet(Z?.itemContent?.tweet_results?.result,V);if(X){z.push(X);continue}for(const Q of Z?.items||[]){const G=extractFolderTweet(Q.item?.itemContent?.tweet_results?.result,V);if(G)z.push(G)}}return{tweets:z,nextCursor:H}}U({site:"twitter",name:"bookmark-folder",access:"read",description:"Read the tweets inside a single Twitter/X bookmark folder. Get the folder id from `opencli twitter bookmark-folders`.",domain:"x.com",strategy:P.COOKIE,browser:!0,args:[{name:"folder-id",positional:!0,type:"string",required:!0,help:"Folder id from `opencli twitter bookmark-folders`."},{name:"limit",type:"int",default:20,help:"Maximum number of bookmarks to return (default 20)."},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the folder by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering."}],columns:["id","author","text","likes","retweets","bookmarks","created_at","url","has_media","media_urls","media_posters"],func:async(J,V)=>{const z=String(V["folder-id"]||"").trim();if(!z||!q.test(z))throw new S(`Invalid folder-id: ${JSON.stringify(V["folder-id"])}. Expected a safe folder ID from \`opencli twitter bookmark-folders\`.`);const H=Number(V.limit??20);if(!Number.isInteger(H)||H<1)throw new S(`Invalid --limit: ${JSON.stringify(V.limit)}. Expected a positive integer.`);const W=(await J.getCookies({url:"https://x.com"})).find((K)=>K.name==="ct0")?.value||null;if(!W)throw new f("x.com","Not logged into x.com (no ct0 cookie)");const Y=await _(J,j,x),Z=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(C)}`,"X-Csrf-Token":W,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),X=[],Q=new Set;let G=null;for(let K=0;K<k&&X.length<H;K++){const h=Math.min(100,H-X.length+10),B=L(Y,z,h,G),b=await J.evaluate(`async () => {
|
|
2
|
+
const r = await fetch(${JSON.stringify(B)}, { headers: ${Z}, credentials: 'include' });
|
|
3
|
+
return r.ok ? await r.json() : { error: r.status };
|
|
4
|
+
}`);if(b?.error){if(X.length===0)throw new O(R("BookmarkFolderTimeline",b.error,`folder=${z}`));break}const{tweets:D,nextCursor:v}=parseBookmarkFolderTimeline(b,Q);X.push(...D);if(!v||v===G)break;G=v}const M=X.slice(0,H);return F(M,V["top-by-engagement"])}});export const __test__={parseBookmarkFolderTimeline,extractFolderTweet,buildFolderTimelineUrl:L,FOLDER_ID_PATTERN:q};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{cli as K,Strategy as L}from"@jackwener/opencli/registry";import{AuthRequiredError as M,CommandExecutionError as P}from"@jackwener/opencli/errors";import{TWITTER_BEARER_TOKEN as Q}from"./utils.js";import{resolveTwitterQueryId as S,describeTwitterApiError as C}from"./shared.js";const D="bookmarkFoldersSlice",j="i78YDd0Tza-dWKw5H2Y7WA",B={rweb_tipjar_consumption_enabled:!1,responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1};function W(G){const J=JSON.stringify({});return`/i/api/graphql/${G}/${D}?variables=${encodeURIComponent(J)}&features=${encodeURIComponent(JSON.stringify(B))}`}export function parseBookmarkFolders(G,J){const V=[],Z=G?.data?.viewer?.bookmark_collections_slice||G?.data?.viewer_v2?.user_results?.result?.bookmark_collections_slice||G?.data?.bookmark_collections_slice||null,$=Z?.items||Z?.timeline?.timeline?.instructions?.flatMap?.((H)=>H.entries||[])||[];for(const H of $){const z=H?.bookmarkCollection||H?.content?.bookmarkCollection||H?.content?.itemContent?.bookmark_collection||H,X=z?.id_str||z?.id||z?.rest_id||"";if(!X||J.has(X))continue;J.add(X);const x=z?.name||z?.collection_name||"",Y=Number(z?.bookmarks_count??z?.items_count??z?.count??0)||0,v=z?.created_at||z?.timestamp_ms||"";V.push({id:String(X),name:String(x),items:Y,created_at:String(v)})}return V}K({site:"twitter",name:"bookmark-folders",access:"read",description:"List your Twitter/X bookmark folders (the user-created collections under Bookmarks). Returns folder id, name, item count, and created_at.",domain:"x.com",strategy:L.COOKIE,browser:!0,args:[],columns:["id","name","items","created_at"],func:async(G)=>{const V=(await G.getCookies({url:"https://x.com"})).find((x)=>x.name==="ct0")?.value||null;if(!V)throw new M("x.com","Not logged into x.com (no ct0 cookie)");const Z=await S(G,D,j),$=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(Q)}`,"X-Csrf-Token":V,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),H=W(Z),z=await G.evaluate(`async () => {
|
|
2
|
+
const r = await fetch(${JSON.stringify(H)}, { headers: ${$}, credentials: 'include' });
|
|
3
|
+
return r.ok ? await r.json() : { error: r.status };
|
|
4
|
+
}`);if(z?.error)throw new P(C("bookmarkFoldersSlice",z.error,"account may not have folder access"));return parseBookmarkFolders(z,new Set)}});export const __test__={parseBookmarkFolders,buildUrl:W};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import{CommandExecutionError as a}from"@jackwener/opencli/errors";import{cli as i,Strategy as s}from"@jackwener/opencli/registry";import{parseTweetUrl as l,buildTwitterArticleScopeSource as n}from"./shared.js";i({site:"twitter",name:"bookmark",access:"write",description:"Bookmark a tweet",domain:"x.com",strategy:s.UI,browser:!0,args:[{name:"url",type:"string",positional:!0,required:!0,help:"Tweet URL to bookmark"}],columns:["status","message"],func:async(e,o)=>{if(!e)throw new a("Browser session required for twitter bookmark");const r=l(o.url);await e.goto(r.url);await e.wait({selector:'[data-testid="primaryColumn"]'});const t=await e.evaluate(`(async () => {
|
|
2
|
+
try {
|
|
3
|
+
${n(r.id)}
|
|
4
|
+
// Article-scoped: on conversation pages multiple bookmark/remove
|
|
5
|
+
// buttons render and a bare querySelector would silently bookmark
|
|
6
|
+
// a different tweet (e.g. the parent of the requested reply).
|
|
7
|
+
let attempts = 0;
|
|
8
|
+
let bookmarkBtn = null;
|
|
9
|
+
let removeBtn = null;
|
|
10
|
+
let targetArticle = null;
|
|
11
|
+
|
|
12
|
+
while (attempts < 20) {
|
|
13
|
+
targetArticle = findTargetArticle();
|
|
14
|
+
removeBtn = targetArticle?.querySelector('[data-testid="removeBookmark"]') || null;
|
|
15
|
+
if (removeBtn) {
|
|
16
|
+
return { ok: true, message: 'Tweet is already bookmarked.' };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
bookmarkBtn = targetArticle?.querySelector('[data-testid="bookmark"]') || null;
|
|
20
|
+
if (bookmarkBtn) break;
|
|
21
|
+
|
|
22
|
+
await new Promise(r => setTimeout(r, 500));
|
|
23
|
+
attempts++;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!bookmarkBtn) {
|
|
27
|
+
return { ok: false, message: 'Could not find Bookmark button on the requested tweet. Are you logged in?' };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
bookmarkBtn.click();
|
|
31
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
32
|
+
|
|
33
|
+
// Verify
|
|
34
|
+
const verifyArticle = findTargetArticle() || targetArticle;
|
|
35
|
+
const verify = verifyArticle?.querySelector('[data-testid="removeBookmark"]');
|
|
36
|
+
if (verify) {
|
|
37
|
+
return { ok: true, message: 'Tweet successfully bookmarked.' };
|
|
38
|
+
} else {
|
|
39
|
+
return { ok: false, message: 'Bookmark action initiated but UI did not update.' };
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return { ok: false, message: e.toString() };
|
|
43
|
+
}
|
|
44
|
+
})()`);if(t.ok)await e.wait(2);return[{status:t.ok?"success":"failed",message:t.message}]}});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import{cli as Y,Strategy as j}from"@jackwener/opencli/registry";import{AuthRequiredError as h,CommandExecutionError as M}from"@jackwener/opencli/errors";import{extractMedia as N,describeTwitterApiError as S}from"./shared.js";import{TWITTER_BEARER_TOKEN as U,applyTopByEngagement as I}from"./utils.js";const G="Fy0QMy4q_aZCpkO0PnyLYw",K=100,O={rweb_video_screen_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!1,rweb_tipjar_consumption_enabled:!1,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,content_disclosure_indicator_enabled:!0,content_disclosure_ai_generated_indicator_enabled:!0,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!1,responsive_web_enhance_cards_enabled:!1};function C(J,V){const z={count:J,includePromotedContent:!1};if(V)z.cursor=V;return`/i/api/graphql/${G}/Bookmarks?variables=${encodeURIComponent(JSON.stringify(z))}&features=${encodeURIComponent(JSON.stringify(O))}`}export function extractBookmarkTweet(J,V){if(!J)return null;const z=J.tweet||J,L=z.legacy||{};if(!z.rest_id||V.has(z.rest_id))return null;V.add(z.rest_id);const Z=z.core?.user_results?.result,f=Z?.legacy?.screen_name||Z?.core?.screen_name||"unknown",$=Z?.legacy?.name||Z?.core?.name||"",H=z.note_tweet?.note_tweet_results?.result?.text;return{id:z.rest_id,author:f,name:$,text:H||L.full_text||"",likes:L.favorite_count||0,retweets:L.retweet_count||0,bookmarks:L.bookmark_count||0,created_at:L.created_at||"",url:`https://x.com/${f}/status/${z.rest_id}`,...N(L)}}export function parseBookmarks(J,V){const z=[];let L=null;const Z=J?.data?.bookmark_timeline_v2?.timeline?.instructions||J?.data?.bookmark_timeline?.timeline?.instructions||[];for(const f of Z)for(const $ of f.entries||[]){const H=$.content;if(H?.entryType==="TimelineTimelineCursor"||H?.__typename==="TimelineTimelineCursor"){if(H.cursorType==="Bottom"||H.cursorType==="ShowMore")L=H.value;continue}if($.entryId?.startsWith("cursor-bottom-")||$.entryId?.startsWith("cursor-showMore-")){L=H?.value||H?.itemContent?.value||L;continue}const W=extractBookmarkTweet(H?.itemContent?.tweet_results?.result,V);if(W){z.push(W);continue}for(const F of H?.items||[]){const X=extractBookmarkTweet(F.item?.itemContent?.tweet_results?.result,V);if(X)z.push(X)}}return{tweets:z,nextCursor:L}}Y({site:"twitter",name:"bookmarks",access:"read",description:"Fetch your Twitter/X bookmarks (the logged-in user's saved tweets, newest first)",domain:"x.com",strategy:j.COOKIE,browser:!0,args:[{name:"limit",type:"int",default:20,help:"Maximum number of bookmarks to return (default 20)."},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (saved-time) ordering."}],columns:["id","author","text","likes","retweets","bookmarks","created_at","url","has_media","media_urls","media_posters"],func:async(J,V)=>{const z=V.limit||20,Z=(await J.getCookies({url:"https://x.com"})).find((b)=>b.name==="ct0")?.value||null;if(!Z)throw new h("x.com","Not logged into x.com (no ct0 cookie)");const f=await J.evaluate(`async () => {
|
|
2
|
+
try {
|
|
3
|
+
const ghResp = await fetch('https://raw.githubusercontent.com/fa0311/twitter-openapi/refs/heads/main/src/config/placeholder.json');
|
|
4
|
+
if (ghResp.ok) {
|
|
5
|
+
const data = await ghResp.json();
|
|
6
|
+
const entry = data['Bookmarks'];
|
|
7
|
+
if (entry && entry.queryId) return entry.queryId;
|
|
8
|
+
}
|
|
9
|
+
} catch {}
|
|
10
|
+
try {
|
|
11
|
+
const scripts = performance.getEntriesByType('resource')
|
|
12
|
+
.filter(r => r.name.includes('client-web') && r.name.endsWith('.js'))
|
|
13
|
+
.map(r => r.name);
|
|
14
|
+
for (const scriptUrl of scripts.slice(0, 15)) {
|
|
15
|
+
try {
|
|
16
|
+
const text = await (await fetch(scriptUrl)).text();
|
|
17
|
+
const re = /queryId:"([A-Za-z0-9_-]+)"[^}]{0,200}operationName:"Bookmarks"/;
|
|
18
|
+
const m = text.match(re);
|
|
19
|
+
if (m) return m[1];
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
22
|
+
} catch {}
|
|
23
|
+
return null;
|
|
24
|
+
}`)||G,$=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(U)}`,"X-Csrf-Token":Z,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),H=[],W=new Set;let F=null;for(let b=0;b<K&&H.length<z;b++){const P=Math.min(100,z-H.length+10),D=C(P,F).replace(G,f),q=await J.evaluate(`async () => {
|
|
25
|
+
const r = await fetch("${D}", { headers: ${$}, credentials: 'include' });
|
|
26
|
+
return r.ok ? await r.json() : { error: r.status };
|
|
27
|
+
}`);if(q?.error){if(H.length===0)throw new M(S("Bookmarks",q.error));break}const{tweets:Q,nextCursor:v}=parseBookmarks(q,W);H.push(...Q);if(!v||v===F)break;F=v}const X=H.slice(0,z);return I(X,V["top-by-engagement"])}});export const __test__={parseBookmarks,extractBookmarkTweet};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import{cli as Y,Strategy as Z}from"@jackwener/opencli/registry";import{CommandExecutionError as _}from"@jackwener/opencli/errors";import{parseTweetUrl as $,buildTwitterArticleScopeSource as q}from"./shared.js";function O(z){return`(() => {
|
|
2
|
+
try {
|
|
3
|
+
const visible = (el) => !!el && (el.offsetParent !== null || el.getClientRects().length > 0);
|
|
4
|
+
${q(z)}
|
|
5
|
+
const targetArticle = findTargetArticle();
|
|
6
|
+
if (!targetArticle) return { article: false, found: false };
|
|
7
|
+
let moreMenu = Array.from(targetArticle.querySelectorAll('[data-testid="caret"]')).find(visible);
|
|
8
|
+
if (!moreMenu) {
|
|
9
|
+
const buttons = Array.from(targetArticle.querySelectorAll('button,[role="button"]'));
|
|
10
|
+
moreMenu = buttons.find((el) => visible(el) && (el.getAttribute('aria-label') || '').trim() === 'More');
|
|
11
|
+
}
|
|
12
|
+
if (!moreMenu) return { article: true, found: false };
|
|
13
|
+
moreMenu.click();
|
|
14
|
+
return { article: true, found: true };
|
|
15
|
+
} catch (e) { return { article: false, found: false }; }
|
|
16
|
+
})()`}function P(){return`(() => {
|
|
17
|
+
try {
|
|
18
|
+
const items = Array.from(document.querySelectorAll('[role="menuitem"]'));
|
|
19
|
+
const deleteBtn = items.find((item) => {
|
|
20
|
+
const text = (item.textContent || '').trim();
|
|
21
|
+
const isList = text.includes('List') || text.includes('列表');
|
|
22
|
+
const isDelete = text.includes('Delete') || text.includes('删除');
|
|
23
|
+
return isDelete && !isList;
|
|
24
|
+
});
|
|
25
|
+
if (!deleteBtn) return { found: false };
|
|
26
|
+
deleteBtn.click();
|
|
27
|
+
return { found: true };
|
|
28
|
+
} catch (e) { return { found: false }; }
|
|
29
|
+
})()`}function Q(){return`(() => {
|
|
30
|
+
try {
|
|
31
|
+
const confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
|
|
32
|
+
if (!confirmBtn) return { found: false };
|
|
33
|
+
confirmBtn.click();
|
|
34
|
+
return { found: true };
|
|
35
|
+
} catch (e) { return { found: false }; }
|
|
36
|
+
})()`}Y({site:"twitter",name:"delete",access:"write",description:"Delete a specific tweet by URL",domain:"x.com",strategy:Z.UI,browser:!0,args:[{name:"url",type:"string",required:!0,positional:!0,help:"The URL of the tweet to delete"}],columns:["status","message"],func:async(z,R)=>{if(!z)throw new _("Browser session required for twitter delete");const J=$(R.url);await z.goto(J.url);await z.wait({selector:'[data-testid="primaryColumn"]'});const H=async(v,V,X=12)=>{let G;for(let N=0;N<X;N++){G=await z.evaluate(v);if(V(G))return G;await z.wait(0.5)}return G},B=await H(O(J.id),(v)=>!!(v&&v.found));if(!B||!B.found){if(B&&B.article===!1)return[{status:"failed",message:"Could not find the tweet card matching the requested URL."}];return[{status:"failed",message:'Could not find the "More" context menu on the matched tweet. Are you sure you are logged in and looking at a valid tweet?'}]}const K=await H(P(),(v)=>!!(v&&v.found));if(!K||!K.found)return[{status:"failed",message:"The matched tweet menu did not contain Delete. This tweet may not belong to you."}];const L=await H(Q(),(v)=>!!(v&&v.found));if(!L||!L.found)return[{status:"failed",message:"Delete confirmation dialog did not appear."}];await z.wait(2);return[{status:"success",message:"Tweet successfully deleted."}]}});export const __test__={buildFindAndClickMoreScript:O,buildClickDeleteScript:P,buildClickConfirmScript:Q};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{ArgumentError as D,AuthRequiredError as f,CommandExecutionError as P,EmptyResultError as F}from"@jackwener/opencli/errors";import{cli as k,Strategy as C}from"@jackwener/opencli/registry";import{TWITTER_BEARER_TOKEN as M,applyTopByEngagement as x}from"./utils.js";import{describeTwitterApiError as L}from"./shared.js";const _="/i/api/2/notifications/device_follow.json",V=200;function q(z){if(z===void 0||z===null||z==="")return 20;const G=Number(z);if(!Number.isInteger(G)||G<1||G>V)throw new D(`--limit must be an integer between 1 and ${V}`);return G}function B(z){const G=new URLSearchParams({include_profile_interstitial_type:"1",include_blocking:"1",include_blocked_by:"1",include_followed_by:"1",include_want_retweets:"1",include_mute_edge:"1",include_can_dm:"1",include_can_media_tag:"1",include_ext_has_nft_avatar:"1",include_ext_is_blue_verified:"1",include_ext_verified_type:"1",skip_status:"1",cards_platform:"Web-12",include_cards:"1",include_ext_alt_text:"true",include_quote_count:"true",include_reply_count:"1",tweet_mode:"extended",include_ext_views:"true",count:String(z)});return`${_}?${G.toString()}`}function W(z){if(!z||!Array.isArray(z.instructions))return null;const G=[];for(const Y of z.instructions){const J=Y?.addEntries?.entries;if(Array.isArray(J))G.push(...J)}return G}function N(z,G,Y){const J=z?.content?.item?.content?.tweet?.id;if(!J)return null;const Z=G?.[J];if(!Z)return null;const $=Y?.[Z.user_id_str]||null;if(typeof $?.screen_name!=="string"||!$.screen_name)return null;return{tweetId:J,tweet:Z,user:$}}function O({tweetId:z,tweet:G,user:Y}){const J=Y.screen_name;return{id:z,author:J,text:G?.full_text||G?.text||"",likes:G?.favorite_count??0,retweets:G?.retweet_count??0,replies:G?.reply_count??0,views:null,created_at:G?.created_at||"",url:`https://x.com/${J}/status/${z}`}}function U(z,G){if(!z?.globalObjects||typeof z.globalObjects!=="object")return null;const Y=z?.globalObjects?.tweets||{},J=z?.globalObjects?.users||{};if(typeof Y!=="object"||typeof J!=="object")return null;const Z=W(z?.timeline);if(!Z)return null;const $=[];let S=0,Q=0;for(const H of Z){if(!Boolean(H?.content?.item?.content?.tweet?.id)){Q++;continue}const K=N(H,Y,J);if(!K){S++;continue}if(G.has(K.tweetId))continue;G.add(K.tweetId);$.push(O(K))}return{rows:$,entryCount:Z.length,unmatchedTweetEntries:S,malformedEntries:Q}}k({site:"twitter",name:"device-follow",access:"read",description:'Read the /i/timeline device-follow notification stream (tweets aggregated under a bell-icon "new posts from @userA and N others" notification)',domain:"x.com",strategy:C.COOKIE,browser:!0,args:[{name:"limit",type:"int",default:20,help:`Maximum number of tweets to return (1-${V}, default 20)`},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank by weighted engagement and return the top N. Default 0 keeps upstream ordering."}],columns:["id","author","text","likes","retweets","replies","views","created_at","url"],func:async(z,G)=>{const Y=q(G.limit),Z=(await z.getCookies({url:"https://x.com"})).find((b)=>b.name==="ct0")?.value||null;if(!Z)throw new f("x.com","Not logged into x.com (no ct0 cookie)");const $=B(Y),S=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(M)}`,"X-Csrf-Token":Z,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),Q=await z.evaluate(`async () => {
|
|
2
|
+
try {
|
|
3
|
+
const r = await fetch("${$}", { method: "GET", headers: ${S}, credentials: 'include' });
|
|
4
|
+
if (!r.ok) return { error: r.status };
|
|
5
|
+
try {
|
|
6
|
+
return await r.json();
|
|
7
|
+
} catch (e) {
|
|
8
|
+
return { errorKind: 'non_json', detail: String(e && e.message || e) };
|
|
9
|
+
}
|
|
10
|
+
} catch (e) {
|
|
11
|
+
return { errorKind: 'exception', detail: String(e && e.message || e) };
|
|
12
|
+
}
|
|
13
|
+
}`);if(Q?.errorKind==="non_json")throw new P(`Twitter device-follow returned non-JSON response: ${Q.detail||"unknown parse error"}`);if(Q?.errorKind==="exception")throw new P(`Twitter device-follow fetch failed: ${Q.detail||"unknown error"}`);if(Q?.error){if(Q.error===401||Q.error===403)throw new f("x.com",`Twitter device-follow returned HTTP ${Q.error}`);throw new P(L("device_follow",Q.error))}const H=U(Q,new Set);if(!H)throw new P("Twitter device-follow response was missing the expected timeline/globalObjects shape.");if(H.malformedEntries>0||H.unmatchedTweetEntries>0)throw new P("Twitter device-follow entries could not be joined to tweet/user objects.");if(H.rows.length===0)throw new F("twitter device-follow","No device-follow notification tweets found.");const K=H.rows.slice(0,Y);return x(K,G["top-by-engagement"])}});export const __test__={buildDeviceFollowUrl:B,extractEntries:W,joinEntryToTweet:N,shapeRow:O,parseDeviceFollow:U,parseLimit:q};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cli as S,Strategy as T}from"@jackwener/opencli/registry";import{CommandExecutionError as G}from"@jackwener/opencli/errors";import{DM_REQUEST_QUERY as b,buildQuery as j,getDmAuth as B,dmApiFetch as M,shapeDmMessages as Q}from"./dm-shared.js";S({site:"twitter",name:"dm-list",access:"read",description:"List recent DM conversations (id, participants, last message, unread state)",domain:"x.com",strategy:T.COOKIE,browser:!0,args:[{name:"limit",type:"int",required:!1,default:20,help:"Maximum number of conversations to return (default 20)"}],columns:["conversation_id","type","participants","last_message","last_time","trusted","muted"],func:async(V,P)=>{const Y=Number(P.limit??20),{ownId:W,headers:x}=await B(V),A="https://x.com/i/api/1.1/dm/inbox_initial_state.json?"+j(b),J=await M(V,A,x);if(J?.error)throw new G(`inbox_initial_state failed: HTTP ${J.error} ${J.body||""}`);const H=J?.inbox_initial_state;if(!H||typeof H!=="object")throw new G("inbox_initial_state missing in response");const X=H.users||{},I=H.conversations||{},K=new Map;for(const q of Q(H.entries,X)){if(!q.conversation_id)continue;const f=K.get(q.conversation_id);if(!f||(q.time||"")>(f.time||""))K.set(q.conversation_id,q)}const Z=Object.entries(I).map(([q,f])=>{const $=(f.participants||[]).map((z)=>String(z.user_id)).filter((z)=>z!==W),N=($.length?$:[W]).map((z)=>{const F=X[z];return F?.screen_name?`@${F.screen_name}`:z}),L=K.get(q)||null;return{conversation_id:q,type:f.type||null,participants:N.join(","),last_message:L?L.text.slice(0,120):null,last_time:L?.time||(f.sort_timestamp?new Date(Number(f.sort_timestamp)).toISOString():null),trusted:f.trusted!==!1,muted:Boolean(f.muted)}});Z.sort((q,f)=>(f.last_time||"").localeCompare(q.last_time||""));return Z.slice(0,Y)}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cli as P,Strategy as X}from"@jackwener/opencli/registry";import{CommandExecutionError as Z}from"@jackwener/opencli/errors";import{DM_REQUEST_QUERY as Y,buildQuery as b,getDmAuth as C,dmApiFetch as R,resolveTwitterUserId as T,shapeDmMessages as j}from"./dm-shared.js";const y=50;P({site:"twitter",name:"dm-read",access:"read",description:"Read message history of one DM conversation (by conversation id from dm-list, or @screen_name / numeric user id for the 1-on-1 conversation)",domain:"x.com",strategy:X.COOKIE,browser:!0,args:[{name:"conversation",type:"string",required:!0,positional:!0,help:"Conversation id like 123-456 (from `dm-list`), or @screen_name / numeric user id of the peer"},{name:"limit",type:"int",required:!1,default:50,help:"Maximum number of messages to return, newest first (default 50)"}],columns:["id","sender","text","time","media_url"],func:async(K,O)=>{const V=Number(O.limit??50),{ownId:$,headers:W}=await C(K),L=String(O.conversation??"").trim();let z;if(/^\d+-\d+$/.test(L))z=L;else z=`${await T(K,L,W)}-${$}`;const B=[];let H=null;for(let f=0;f<y&&B.length<V;f++){const F=`https://x.com/i/api/1.1/dm/conversation/${z}.json?`+b(Y,{context:"FETCH_DM_CONVERSATION_HISTORY",include_conversation_info:"true",...H?{max_id:H}:{}}),J=await R(K,F,W);if(J?.error){if(B.length===0)throw new Z(`dm/conversation failed for ${z}: HTTP ${J.error} ${J.body||""}`);break}const q=J?.conversation_timeline;if(!q)throw new Z("conversation_timeline missing in response");const G=j(q.entries,q.users);for(const N of G){if(!N.conversation_id)N.conversation_id=z;B.push(N)}if(q.status==="AT_END"||!q.min_entry_id||q.min_entry_id===H)break;H=q.min_entry_id}return B.slice(0,V).map((f)=>({id:f.id,sender:f.sender,text:f.text,time:f.time,media_url:f.media_url}))}});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{cli as $,Strategy as q}from"@jackwener/opencli/registry";import{ArgumentError as L,CommandExecutionError as W}from"@jackwener/opencli/errors";import{DM_SEND_QUERY as N,buildQuery as P,getDmAuth as R,resolveTwitterUserId as T,shapeDmMessages as Y}from"./dm-shared.js";import{assertLiteralContent as A}from"../_shared/content-guard.js";$({site:"twitter",name:"dm-send",access:"write",description:"Send a direct message to one user (by @screen_name, numeric user id, or conversation id from dm-list)",domain:"x.com",strategy:q.COOKIE,browser:!0,args:[{name:"user",type:"string",required:!0,positional:!0,help:"Recipient: @screen_name, numeric user id, or a conversation id like 123-456 from `dm-list`"},{name:"text",type:"string",required:!0,positional:!0,help:"Message text to send"},{name:"reply-to",type:"string",required:!1,help:"Message id to reply to (from `dm-read`)"}],columns:["id","conversation_id","text","time","status"],func:async(G,B)=>{const H=String(B.text??"").trim();A(H,{label:"私信内容"});if(!H)throw new L("Message text must not be empty");const{ownId:X,headers:K}=await R(G),J=String(B.user??"").trim();let F;if(/^\d+-\d+$/.test(J))F=J;else F=`${await T(G,J,K)}-${X}`;const O={cards_platform:"Web-12",conversation_id:F,dm_users:!1,include_cards:1,include_quote_count:!0,recipient_ids:!1,text:H,audio_only_media_attachment:!1};if(B["reply-to"])O.reply_to_dm_id=String(B["reply-to"]).trim();const Z="https://x.com/i/api/1.1/dm/new2.json?"+P(N),f=await G.evaluate(`async () => {
|
|
2
|
+
const body = ${JSON.stringify(O)};
|
|
3
|
+
body.request_id = crypto.randomUUID();
|
|
4
|
+
const r = await fetch(${JSON.stringify(Z)}, {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
headers: Object.assign(${JSON.stringify(K)}, { 'Content-Type': 'application/json' }),
|
|
7
|
+
credentials: 'include',
|
|
8
|
+
body: JSON.stringify(body),
|
|
9
|
+
});
|
|
10
|
+
const text = await r.text();
|
|
11
|
+
if (!r.ok) return { error: r.status, body: text.slice(0, 300) };
|
|
12
|
+
try { return JSON.parse(text); } catch { return { error: 'bad-json', body: text.slice(0, 300) }; }
|
|
13
|
+
}`);if(f?.error)throw new W(`dm/new2.json failed: HTTP ${f.error} ${f.body||""}`);const V=Y(f.entries,f.users);if(!V.length)throw new W(`DM send returned no message entry (response keys: ${Object.keys(f||{}).join(",")})`);return V.map((z)=>({id:z.id,conversation_id:z.conversation_id||F,text:z.text,time:z.time,status:"sent"}))}});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{AuthRequiredError as W,CommandExecutionError as b}from"@jackwener/opencli/errors";import{TWITTER_BEARER_TOKEN as f}from"./utils.js";import{resolveTwitterOperationMetadata as q}from"./shared.js";export const DM_REQUEST_QUERY={nsfw_filtering_enabled:"false",filter_low_quality:"true",include_quality:"all",include_profile_interstitial_type:"1",include_blocking:"1",include_blocked_by:"1",include_followed_by:"1",include_want_retweets:"1",include_mute_edge:"1",include_can_dm:"1",include_can_media_tag:"1",include_ext_is_blue_verified:"1",include_ext_verified_type:"1",include_ext_profile_image_shape:"1",skip_status:"1",dm_secret_conversations_enabled:"false",krs_registration_enabled:"false",cards_platform:"Web-12",include_cards:"1",include_ext_alt_text:"true",include_ext_limited_action_results:"true",include_quote_count:"true",include_reply_count:"1",tweet_mode:"extended",include_ext_views:"true",dm_users:"true",include_groups:"true",include_inbox_timelines:"true",include_ext_media_color:"true",supports_reactions:"true",supports_edit:"true",include_ext_edit_control:"true",include_ext_business_affiliations_label:"true",include_ext_parody_commentary_fan_label:"true",ext:"mediaColor,altText,businessAffiliationsLabel,mediaStats,highlightedLabel,parodyCommentaryFanLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article"},DM_SEND_QUERY={ext:"mediaColor,altText,mediaStats,highlightedLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article",include_ext_alt_text:"true",include_ext_limited_action_results:"true",include_reply_count:"1",tweet_mode:"extended",include_ext_views:"true",include_groups:"true",include_inbox_timelines:"true",include_ext_media_color:"true",supports_reactions:"true",supports_edit:"true"};export function buildQuery(J,H={}){const G={...J,...H};return Object.entries(G).filter(([,D])=>D!==null&&D!==void 0).map(([D,z])=>`${encodeURIComponent(D)}=${encodeURIComponent(String(z))}`).join("&")}export async function getDmAuth(J){const H=await J.getCookies({url:"https://x.com"}),G=H.find((P)=>P.name==="ct0")?.value||null;if(!G)throw new W("x.com","Not logged into x.com (no ct0 cookie)");const D=H.find((P)=>P.name==="twid")?.value||"",z=decodeURIComponent(D).replace(/^u=/,"").trim();if(!/^\d+$/.test(z))throw new W("x.com","Cannot resolve own user id from twid cookie");const V={Authorization:`Bearer ${decodeURIComponent(f)}`,"X-Csrf-Token":G,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"};return{ct0:G,ownId:z,headers:V}}export async function dmApiFetch(J,H,G,D=null){const z=D?`, { method: ${JSON.stringify(D.method||"GET")}, headers: Object.assign(${JSON.stringify(G)}, ${JSON.stringify(D.extraHeaders||{})}), credentials: 'include', body: ${JSON.stringify(D.body??null)} }`:`, { headers: ${JSON.stringify(G)}, credentials: 'include' }`;return J.evaluate(`async () => {
|
|
2
|
+
const r = await fetch(${JSON.stringify(H)}${z});
|
|
3
|
+
const text = await r.text();
|
|
4
|
+
if (!r.ok) return { error: r.status, body: text.slice(0, 300) };
|
|
5
|
+
try { return JSON.parse(text); } catch { return { error: 'bad-json', body: text.slice(0, 300) }; }
|
|
6
|
+
}`)}const Q={queryId:"IGgvgiOx4QZndDHuD3x9TQ",features:{hidden_profile_subscriptions_enabled:!0,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!1,rweb_tipjar_consumption_enabled:!1,verified_phone_label_enabled:!1,subscriptions_verification_info_is_identity_verified_enabled:!0,subscriptions_verification_info_verified_since_enabled:!0,highlights_tweets_tab_ui_enabled:!0,responsive_web_twitter_article_notes_tab_enabled:!0,subscriptions_feature_can_gift_premium:!0,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,responsive_web_graphql_timeline_navigation_enabled:!0},fieldToggles:{withPayments:!1,withAuxiliaryUserLabels:!0}};export async function resolveTwitterUserId(J,H,G){const D=String(H||"").trim().replace(/^@/,"");if(/^\d+$/.test(D))return D;if(!/^[A-Za-z0-9_]{1,15}$/.test(D))throw new b(`Invalid user: ${JSON.stringify(H)}. Expected @screen_name or numeric user id.`);const z=await q(J,"UserByScreenName",Q.queryId),V=Object.keys(z.features||{}).length?z.features:Q.features,P=Object.keys(z.fieldToggles||{}).length?z.fieldToggles:Q.fieldToggles,X=`https://x.com/i/api/graphql/${z.queryId}/UserByScreenName?`+buildQuery({variables:JSON.stringify({screen_name:D}),features:JSON.stringify(V),fieldToggles:JSON.stringify(P)}),Z=await dmApiFetch(J,X,G);if(Z?.error)throw new b(`UserByScreenName failed for @${D}: HTTP ${Z.error} ${Z.body||""}`);const $=Z?.data?.user?.result?.rest_id;if(typeof $!=="string"||!$)throw new b(`User @${D} not found or unavailable`);return $}export function shapeDmMessages(J,H){const G=[];for(const D of J||[]){const z=D?.message?.message_data;if(!z?.id)continue;const V=H?.[z.sender_id],P=z.attachment?.photo?.media_url_https||z.attachment?.video?.video_info?.variants?.find((X)=>X?.content_type==="video/mp4")?.url||z.attachment?.animated_gif?.video_info?.variants?.find((X)=>X?.content_type==="video/mp4")?.url||null;G.push({id:z.id,conversation_id:D.message.conversation_id||null,sender_id:z.sender_id||null,sender:V?.screen_name?`@${V.screen_name}`:z.sender_id||null,text:z.text||"",time:z.time?new Date(Number(z.time)).toISOString():null,media_url:P})}return G}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import{cli as o,Strategy as r}from"@jackwener/opencli/registry";import{ArgumentError as D,AuthRequiredError as h,CliError as t,CommandExecutionError as X,EmptyResultError as T}from"@jackwener/opencli/errors";import{formatCookieHeader as C}from"@jackwener/opencli/download";import{downloadMedia as a}from"@jackwener/opencli/download/media-download";import{resolveTwitterOperationMetadata as R,normalizeTwitterGraphqlPayload as _,unwrapBrowserResult as y,normalizeTwitterScreenName as e,extractMedia as jj,parseTweetUrl as Fj}from"./shared.js";import{TWITTER_BEARER_TOKEN as Gj}from"./utils.js";const I="9EovraBTXJYGSEQXZqlLmQ",k="IGgvgiOx4QZndDHuD3x9TQ",E=100,O={rweb_video_screen_enabled:!0,rweb_cashtags_enabled:!0,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!0,rweb_tipjar_consumption_enabled:!0,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,responsive_web_grok_analyze_button_fetch_trends_enabled:!1,responsive_web_grok_analyze_post_followups_enabled:!0,rweb_cashtags_composer_attachment_enabled:!0,responsive_web_jetfuel_frame:!0,responsive_web_grok_share_attachment_enabled:!0,responsive_web_grok_annotations_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,rweb_conversational_replies_downvote_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,content_disclosure_indicator_enabled:!0,content_disclosure_ai_generated_indicator_enabled:!0,responsive_web_grok_show_grok_translated_post:!1,responsive_web_grok_analysis_button_from_backend:!0,post_ctas_fetch_enabled:!1,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!0,responsive_web_grok_image_annotation_enabled:!0,responsive_web_grok_imagine_annotation_enabled:!0,responsive_web_grok_community_note_auto_translation_is_enabled:!1,responsive_web_enhance_cards_enabled:!1},f={withPayments:!0,withAuxiliaryUserLabels:!0,withArticleRichContentState:!0,withArticlePlainText:!0,withArticleSummaryText:!0,withArticleVoiceOver:!0,withGrokAnalyze:!0,withDisallowedReplyControls:!0},S={hidden_profile_subscriptions_enabled:!0,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!0,rweb_tipjar_consumption_enabled:!0,responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,subscriptions_verification_info_is_identity_verified_enabled:!0,subscriptions_verification_info_verified_since_enabled:!0,highlights_tweets_tab_ui_enabled:!0,responsive_web_twitter_article_notes_tab_enabled:!0,subscriptions_feature_can_gift_premium:!0,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,responsive_web_graphql_timeline_navigation_enabled:!0},N={withPayments:!0,withAuxiliaryUserLabels:!0},Hj={queryId:I,features:O,fieldToggles:f},Jj={queryId:k,features:S,fieldToggles:N};function U(j){const F=Number(j??10);if(!Number.isInteger(F)||F<1||F>1000)throw new D("--limit must be an integer between 1 and 1000");return F}function x(j,F){const G=j-F;if(G<=0)return 0;const H=G+10;if(H>100)return 100;return H}async function w(j,F){return(await a(j,F)).map((H,K)=>{const V=j[K]||{};return{index:H.index,tweet_id:V.tweet_id||"",url:V.url||"",type:H.type,status:H.status,size:H.size}})}function Kj(j){if(typeof j==="string")return{queryId:j,features:O,fieldToggles:f};return{queryId:j?.queryId||I,features:j?.features||O,fieldToggles:j?.fieldToggles||f}}function Vj(j){if(typeof j==="string")return{queryId:j,features:S,fieldToggles:N};return{queryId:j?.queryId||k,features:j?.features||S,fieldToggles:j?.fieldToggles||N}}function c(j,F,G){const H=G.fieldToggles||{},K=[`variables=${encodeURIComponent(JSON.stringify(F))}`,`features=${encodeURIComponent(JSON.stringify(G.features||{}))}`];if(Object.keys(H).length>0)K.push(`fieldToggles=${encodeURIComponent(JSON.stringify(H))}`);return`${j}?${K.join("&")}`}function v(j,F,G,H){const K=Kj(j),V={userId:F,count:G,includePromotedContent:!1,withClientEventToken:!1,withBirdwatchNotes:!1,withVoice:!0};if(H)V.cursor=H;return c(`/i/api/graphql/${K.queryId}/UserMedia`,V,K)}function d(j,F){const G=Vj(j),H={screen_name:F,withSafetyModeUserFields:!0};return c(`/i/api/graphql/${G.queryId}/UserByScreenName`,H,G)}function g(j){if(!j)return"unknown";if(/video\.twimg\.com|\.mp4(\?|$)|\.m3u8(\?|$)/.test(j))return"video";return"image"}function p(j,F){if(!j||typeof j!=="object"||Array.isArray(j))throw new X(`Twitter ${F} returned malformed payload`);return j}function Wj(j,F,G){if(F===401||F===403)throw new h("x.com",`Twitter ${j} requires an authenticated x.com session`);if(F===404)throw new T(`twitter download ${j}`,G||"Twitter returned not found");const H=F?`HTTP ${F}`:"fetch failed";throw new X(`Twitter ${j} fetch failed: ${H}${G?` - ${G}`:""}`)}function m(j,F){const G=p(y(j),F);if(G.ok===!0)return G.payload;if(G.ok===!1)Wj(F,Number(G.status)||0,typeof G.error==="string"?G.error:"");throw new X(`Twitter ${F} returned malformed fetch result`)}function Xj(j){const F=p(j,"UserMedia");if(Array.isArray(F.errors)&&F.errors.length>0)throw new X(`Twitter UserMedia returned GraphQL errors: ${JSON.stringify(F.errors).slice(0,200)}`);const G=F.data?.user?.result;if(!G||typeof G!=="object")throw new X("Twitter UserMedia returned malformed user result");const H=G.timeline_v2?.timeline?.instructions||G.timeline?.timeline?.instructions;if(!Array.isArray(H))throw new X("Twitter UserMedia returned malformed timeline instructions");return F}function n(j,F){const G=[];let H=null;const K=Xj(j).data.user.result,Z=[K.timeline_v2?.timeline?.instructions,K.timeline?.timeline?.instructions].filter(Array.isArray).flat(),Q=(J)=>{if(!J||typeof J!=="object")return;if(J.type==="TimelinePinEntry")return;if(J.tweet_results?.result){const W=J.tweet_results.result,b=W.__typename==="TweetWithVisibilityResults"&&W.tweet?W.tweet:W.tweet||W,$=typeof b.rest_id==="string"||typeof b.rest_id==="number"?String(b.rest_id):"";if(!$)throw new X("Twitter UserMedia returned a tweet without rest_id");if(!F.has($)){F.add($);const{media_urls:B}=jj(b.legacy||{});for(const P of B)G.push({tweet_id:$,url:P,type:g(P)})}}if((J.entryType==="TimelineTimelineCursor"||J.__typename==="TimelineTimelineCursor")&&(J.cursorType==="Bottom"||J.cursorType==="ShowMore")&&J.value)H=J.value;if(Array.isArray(J)){for(const W of J)Q(W);return}for(const W of Object.values(J))if(W&&typeof W==="object")Q(W)};Q(Z);return{items:G,nextCursor:H}}o({site:"twitter",name:"download",access:"read",description:"Download Twitter/X media (images and videos). Provide either <username> to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.",domain:"x.com",strategy:r.COOKIE,browser:!0,args:[{name:"username",positional:!0,help:"Twitter username (with or without @) to scan their profile media. Either <username> or --tweet-url is required."},{name:"tweet-url",help:"Single tweet URL to download. Use this OR <username>, not both required at once."},{name:"limit",type:"int",default:10,help:"Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used."},{name:"output",default:"./twitter-downloads",help:"Output directory (default ./twitter-downloads). A per-source subdir is created inside."}],columns:["index","tweet_id","url","type","status","size"],func:async(j,F)=>{try{const G=String(F.username??"").trim(),H=String(F["tweet-url"]??"").trim(),K=F.output;if(!G&&!H)throw new D("twitter download requires either <username> or --tweet-url");if(G&&H)throw new D("Use either <username> or --tweet-url, not both");if(H)return $j(j,H,K);const V=U(F.limit),Z=e(G);if(!Z)throw new D("twitter download username must be a valid Twitter/X handle","Example: opencli twitter download @jack --limit 20");return Zj(j,Z,V,K)}catch(G){if(G instanceof t)throw G;throw new X(`twitter download failed: ${G?.message??String(G)}`)}}});async function Zj(j,F,G,H){await j.goto(`https://x.com/${F}`);await j.wait({selector:'[data-testid="primaryColumn"]'});const K=await j.getCookies({url:"https://x.com"}),V=K.find((Y)=>Y.name==="ct0")?.value||null;if(!V)throw new h("x.com","Not logged into x.com (no ct0 cookie)");const Z=await R(j,"UserMedia",Hj),Q=await R(j,"UserByScreenName",Jj),J=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(Gj)}`,"X-Csrf-Token":V,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),W=d(Q,F),b=m(await j.evaluate(`async () => {
|
|
2
|
+
try {
|
|
3
|
+
const resp = await fetch("${W}", { headers: ${J}, credentials: 'include' });
|
|
4
|
+
if (!resp.ok) return { ok: false, status: resp.status };
|
|
5
|
+
const payload = await resp.json();
|
|
6
|
+
return { ok: true, payload };
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return { ok: false, error: err?.message ?? String(err) };
|
|
9
|
+
}
|
|
10
|
+
}`)),$=_(b);if(Array.isArray($?.errors)&&$.errors.length>0)throw new X(`Twitter UserByScreenName returned GraphQL errors: ${JSON.stringify($.errors).slice(0,200)}`);const B=$?.data?.user?.result?.rest_id;if(!B)throw new T(`twitter download @${F}`,`Could not resolve @${F}`);const P=new Set,q=[];let L=null,z=!1;for(let Y=0;Y<E&&q.length<G;Y++){const M=x(G,q.length);if(M===0)break;const l=v(Z,B,M,L),u=_(m(await j.evaluate(`async () => {
|
|
11
|
+
try {
|
|
12
|
+
const r = await fetch("${l}", { headers: ${J}, credentials: 'include' });
|
|
13
|
+
if (!r.ok) return { ok: false, status: r.status };
|
|
14
|
+
return { ok: true, payload: await r.json() };
|
|
15
|
+
} catch (err) {
|
|
16
|
+
return { ok: false, error: err?.message ?? String(err) };
|
|
17
|
+
}
|
|
18
|
+
}`))),{items:s,nextCursor:A}=n(u,P);q.push(...s);z=Boolean(A);if(!A)break;if(A===L)throw new X("Twitter UserMedia pagination returned the same cursor twice");L=A}if(q.length===0)throw new T(`@${F} has no media`,"Account may be private, suspended, or have no media posts");if(q.length<G&&z)throw new X(`Twitter UserMedia pagination reached the ${E}-page safety cap before collecting ${G} media items`);const i=q.slice(0,G);return w(i,{output:H,subdir:F,cookies:C(K),browserCookies:K,filenamePrefix:F,ytdlpExtraArgs:["--merge-output-format","mp4"]})}async function $j(j,F,G){const H=Fj(F);await j.goto(H.url);await j.wait(3);const K=y(await j.evaluate(`
|
|
19
|
+
(() => {
|
|
20
|
+
const out = [];
|
|
21
|
+
document.querySelectorAll('img[src*="pbs.twimg.com/media"]').forEach(img => {
|
|
22
|
+
let src = img.src || '';
|
|
23
|
+
src = src.replace(/&name=\\w+$/, '&name=large');
|
|
24
|
+
if (!src.includes('&name=')) src = src + '&name=large';
|
|
25
|
+
out.push({ type: 'image', url: src });
|
|
26
|
+
});
|
|
27
|
+
document.querySelectorAll('video').forEach(video => {
|
|
28
|
+
const src = video.src || '';
|
|
29
|
+
if (src) out.push({ type: 'video', url: src });
|
|
30
|
+
});
|
|
31
|
+
document.querySelectorAll('[data-testid="videoPlayer"]').forEach(player => {
|
|
32
|
+
const tweetLink = player.closest('article')?.querySelector('a[href*="/status/"]');
|
|
33
|
+
const href = tweetLink?.getAttribute('href') || '';
|
|
34
|
+
if (href) out.push({ type: 'video-tweet', url: 'https://x.com' + href });
|
|
35
|
+
});
|
|
36
|
+
return out;
|
|
37
|
+
})()
|
|
38
|
+
`));if(!Array.isArray(K))throw new X("Twitter tweet media extraction returned malformed payload");if(K.length===0)throw new T(`twitter download ${H.id}`,"No media found in the tweet");const V=await j.getCookies({domain:"x.com"}),Z=new Set,Q=K.filter((J)=>{if(Z.has(J.url))return!1;Z.add(J.url);return!0}).map((J)=>{return{...J,tweet_id:H.id}});return w(Q,{output:G,subdir:"tweets",cookies:C(V),browserCookies:V,filenamePrefix:"tweet",ytdlpExtraArgs:["--merge-output-format","mp4"]})}export const __test__={buildUserMediaUrl:v,buildUserByScreenNameUrl:d,parseUserMedia:n,classifyMediaUrl:g,requireLimit:U,nextUserMediaFetchCount:x};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import{ArgumentError as a,AuthRequiredError as d,CommandExecutionError as y}from"@jackwener/opencli/errors";import{cli as h,Strategy as p}from"@jackwener/opencli/registry";import{unwrapBrowserResult as u}from"./shared.js";const g=/^[A-Za-z0-9_]{1,15}$/,c=3000,f=60000;export function parseBatchUsernames(t){const e=String(t||"").trim();if(!e)throw new a("At least one Twitter/X username is required");const o=[],r=new Set;for(const i of e.split(",")){const s=i.trim().replace(/^@+/,"");if(!s)continue;if(!g.test(s))throw new a(`Invalid Twitter/X username: ${JSON.stringify(i.trim())}`);const n=s.toLowerCase();if(r.has(n))continue;r.add(n);o.push(s)}if(!o.length)throw new a("At least one Twitter/X username is required");return o}async function w(t,e){return u(await t.evaluate(`(async () => {
|
|
2
|
+
try {
|
|
3
|
+
let attempts = 0;
|
|
4
|
+
while (attempts < 20) {
|
|
5
|
+
const unfollowBtn = document.querySelector('[data-testid$="-unfollow"]');
|
|
6
|
+
if (unfollowBtn) {
|
|
7
|
+
return { ok: true, status: 'noop', message: 'Already following @${e}.' };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const followBtn = document.querySelector('[data-testid$="-follow"]');
|
|
11
|
+
if (followBtn) {
|
|
12
|
+
return { ok: false, followButtonVisible: true };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
await new Promise(r => setTimeout(r, 500));
|
|
16
|
+
attempts++;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { ok: false, followButtonVisible: false };
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return { ok: false, message: e.toString() };
|
|
22
|
+
}
|
|
23
|
+
})()`))}async function k(t,e){return u(await t.evaluate(`(async () => {
|
|
24
|
+
try {
|
|
25
|
+
const followBtn = document.querySelector('[data-testid$="-follow"]');
|
|
26
|
+
if (!followBtn) {
|
|
27
|
+
return { ok: false, retryAfterRefresh: true, message: 'Could not find Follow button after loading profile.' };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
followBtn.click();
|
|
31
|
+
for (let attempts = 0; attempts < 20; attempts++) {
|
|
32
|
+
await new Promise(r => setTimeout(r, 500));
|
|
33
|
+
const verify = document.querySelector('[data-testid$="-unfollow"]');
|
|
34
|
+
if (verify) {
|
|
35
|
+
return { ok: true, status: 'success', message: 'Successfully followed @${e}.' };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { ok: false, retryAfterRefresh: true, message: 'Follow action initiated but UI did not update.' };
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return { ok: false, message: e.toString() };
|
|
42
|
+
}
|
|
43
|
+
})()`))}export async function followOne(t,e){await t.goto(`https://x.com/${e}`);await t.wait({selector:'[data-testid="primaryColumn"]'});let o=await w(t,e);if(!o.ok&&o.followButtonVisible)o=await k(t,e);if(!o.ok&&o.retryAfterRefresh){await t.goto(`https://x.com/${e}`);await t.wait({selector:'[data-testid="primaryColumn"]'});const r=await w(t,e);if(r.ok)o={...r,status:"success",message:`Successfully followed @${e}.`}}if(!o.ok&&!o.message)o={...o,message:"Could not find Follow button. Are you logged in?"};if(o.ok)await t.wait(1);return{username:e,status:o.ok?o.status:"failed",message:o.message}}export function parseDelayMs(t){if(t===void 0||t===null||t==="")return c;const e=Number(t);if(!Number.isInteger(e)||e<0||e>f)throw new a(`delay-ms must be an integer between 0 and ${f}`);return e}h({site:"twitter",name:"follow-batch",access:"write",description:"Follow multiple Twitter/X users from a comma-separated username list",domain:"x.com",strategy:p.UI,browser:!0,args:[{name:"usernames",type:"string",positional:!0,required:!0,help:"Comma-separated Twitter/X screen names, with or without @"},{name:"delay-ms",type:"int",default:c,help:"Delay between follow attempts in milliseconds"}],columns:["username","status","message"],func:async(t,e)=>{if(!t)throw new y("Browser session required for twitter follow-batch");const o=parseBatchUsernames(e.usernames),r=parseDelayMs(e["delay-ms"]);if(!(await t.getCookies({url:"https://x.com"})).find((l)=>l.name==="ct0")?.value)throw new d("x.com","Not logged into x.com (no ct0 cookie)");const n=[];for(const[l,m]of o.entries()){if(l>0&&r>0)await t.wait(r/1000);n.push(await followOne(t,m))}return n}});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import{CommandExecutionError as l}from"@jackwener/opencli/errors";import{cli as s,Strategy as a}from"@jackwener/opencli/registry";s({site:"twitter",name:"follow",access:"write",description:"Follow a Twitter user",domain:"x.com",strategy:a.UI,browser:!0,args:[{name:"username",type:"string",positional:!0,required:!0,help:"Twitter screen name (without @)"}],columns:["status","message"],func:async(e,r)=>{if(!e)throw new l("Browser session required for twitter follow");const t=r.username.replace(/^@/,"");await e.goto(`https://x.com/${t}`);await e.wait({selector:'[data-testid="primaryColumn"]'});const o=await e.evaluate(`(async () => {
|
|
2
|
+
try {
|
|
3
|
+
let attempts = 0;
|
|
4
|
+
let followBtn = null;
|
|
5
|
+
let unfollowTestId = null;
|
|
6
|
+
|
|
7
|
+
while (attempts < 20) {
|
|
8
|
+
// Check if already following (button shows screen_name-unfollow)
|
|
9
|
+
unfollowTestId = document.querySelector('[data-testid$="-unfollow"]');
|
|
10
|
+
if (unfollowTestId) {
|
|
11
|
+
return { ok: true, message: 'Already following @${t}.' };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Look for the Follow button
|
|
15
|
+
followBtn = document.querySelector('[data-testid$="-follow"]');
|
|
16
|
+
if (followBtn) break;
|
|
17
|
+
|
|
18
|
+
await new Promise(r => setTimeout(r, 500));
|
|
19
|
+
attempts++;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!followBtn) {
|
|
23
|
+
return { ok: false, message: 'Could not find Follow button. Are you logged in?' };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
followBtn.click();
|
|
27
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
28
|
+
|
|
29
|
+
// Verify
|
|
30
|
+
const verify = document.querySelector('[data-testid$="-unfollow"]');
|
|
31
|
+
if (verify) {
|
|
32
|
+
return { ok: true, message: 'Successfully followed @${t}.' };
|
|
33
|
+
} else {
|
|
34
|
+
return { ok: false, message: 'Follow action initiated but UI did not update.' };
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return { ok: false, message: e.toString() };
|
|
38
|
+
}
|
|
39
|
+
})()`);if(o.ok)await e.wait(2);return[{status:o.ok?"success":"failed",message:o.message}]}});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import{ArgumentError as V,AuthRequiredError as $,selectorError as T,EmptyResultError as _,CommandExecutionError as q}from"@jackwener/opencli/errors";import{cli as v,Strategy as j}from"@jackwener/opencli/registry";import{normalizeTwitterScreenName as D,unwrapBrowserResult as M}from"./shared.js";async function b(G){const L=`() => {
|
|
2
|
+
const cells = document.querySelectorAll('[data-testid="UserCell"]');
|
|
3
|
+
const out = [];
|
|
4
|
+
for (const cell of cells) {
|
|
5
|
+
// Collect i18n-variable UI strings to strip from the cell text.
|
|
6
|
+
const stripTexts = new Set();
|
|
7
|
+
const buttons = cell.querySelectorAll(
|
|
8
|
+
'[data-testid$="-follow"],[data-testid$="-unfollow"],[data-testid="userFollowIndicator"]'
|
|
9
|
+
);
|
|
10
|
+
for (const el of buttons) {
|
|
11
|
+
const t = (el.innerText || '').trim();
|
|
12
|
+
if (t) stripTexts.add(t);
|
|
13
|
+
}
|
|
14
|
+
const lines = (cell.innerText || '')
|
|
15
|
+
.split('\\n')
|
|
16
|
+
.map(s => s.trim())
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.filter(l => !stripTexts.has(l));
|
|
19
|
+
// Pull the @handle line; fall back to UserAvatar-Container-<handle>.
|
|
20
|
+
let screen_name = '';
|
|
21
|
+
const remaining = [];
|
|
22
|
+
for (const l of lines) {
|
|
23
|
+
if (!screen_name && l.startsWith('@')) {
|
|
24
|
+
screen_name = l.slice(1).split(/\\s/)[0];
|
|
25
|
+
} else {
|
|
26
|
+
remaining.push(l);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!screen_name) {
|
|
30
|
+
const av = cell.querySelector('[data-testid^="UserAvatar-Container-"]');
|
|
31
|
+
const tid = av ? av.getAttribute('data-testid') || '' : '';
|
|
32
|
+
if (tid.startsWith('UserAvatar-Container-')) {
|
|
33
|
+
screen_name = tid.slice('UserAvatar-Container-'.length);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// First non-handle line is display name (may equal handle when the user hasn't set one).
|
|
37
|
+
const name = remaining[0] || screen_name;
|
|
38
|
+
// Lines past the display name form the bio.
|
|
39
|
+
const bio = remaining.slice(1).join(' ').replace(/\\s+/g, ' ').trim();
|
|
40
|
+
if (screen_name) {
|
|
41
|
+
out.push({ screen_name, name, bio });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}`;return G.evaluate(L)}function W(G){return D(G)}v({site:"twitter",name:"followers",access:"read",description:"Get accounts following a Twitter/X user (defaults to the logged-in user when no user is given)",domain:"x.com",strategy:j.UI,browser:!0,args:[{name:"user",positional:!0,type:"string",required:!1,help:"Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account."},{name:"limit",type:"int",default:50,help:"Maximum number of follower rows to return (default 50). Must be a positive integer."}],columns:["screen_name","name","bio"],func:async(G,L)=>{const J=L.limit;if(!Number.isInteger(J)||J<=0)throw new V("limit must be a positive integer");const X=String(L.user??"").trim();let H=W(X);if(X&&!H)throw new V("twitter followers user must be a valid Twitter/X handle","Example: opencli twitter followers @elonmusk --limit 100");if(!H){await G.goto("https://x.com/home");await G.wait({selector:'[data-testid="primaryColumn"]'});const I=M(await G.evaluate(`() => {
|
|
46
|
+
const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
|
|
47
|
+
return link ? link.getAttribute('href') : null;
|
|
48
|
+
}`));if(!I||typeof I!=="string")throw new $("x.com","Could not find logged-in user profile link. Are you logged in?");H=W(I);if(!H)throw new $("x.com","Could not find logged-in user profile link. Are you logged in?")}if(!H)throw new V("twitter followers user cannot be empty","Example: opencli twitter followers @elonmusk --limit 100");await G.goto(`https://x.com/${H}`);await G.wait(3);const B=JSON.stringify(H);if(!await G.evaluate(`() => {
|
|
49
|
+
const target = ${B};
|
|
50
|
+
const selectors = [
|
|
51
|
+
'a[href="/' + target + '/followers"]',
|
|
52
|
+
'a[href="/' + target + '/verified_followers"]',
|
|
53
|
+
];
|
|
54
|
+
for (const sel of selectors) {
|
|
55
|
+
const link = document.querySelector(sel);
|
|
56
|
+
if (link) { link.click(); return true; }
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}`))throw T("Twitter followers link","Twitter may have changed the layout.");await G.wait({selector:'[data-testid="UserCell"]',timeout:1e4});const K=[],Y=new Set;let Q=0;while(K.length<J&&Q<3){const I=await b(G);if(!Array.isArray(I))throw new q("Twitter followers extraction returned malformed rows");const Z=I.filter((P)=>!Y.has(P.screen_name));for(const P of Z){Y.add(P.screen_name);K.push(P)}if(Z.length===0)Q++;else Q=0;if(K.length>=J)break;await G.autoScroll({times:1,delayMs:500});await G.wait(2)}if(K.length===0)throw new _("twitter followers",`No followers found for @${H}`);return K.slice(0,J)}});export const __test__={extractFollowersFromDOM:b,normalizeScreenName:W};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{cli as U,Strategy as w}from"@jackwener/opencli/registry";import{ArgumentError as v,AuthRequiredError as j,CommandExecutionError as b,EmptyResultError as C}from"@jackwener/opencli/errors";import{looksLikePrivateTwitterTimeline as E,normalizeTwitterScreenName as m,resolveTwitterQueryId as x,sanitizeQueryId as p,unwrapBrowserResult as B,describeTwitterApiError as d}from"./shared.js";import{TWITTER_BEARER_TOKEN as g}from"./utils.js";const c="F42cDX8PDFxkbjjq6JrM2w",i="IGgvgiOx4QZndDHuD3x9TQ",l=100,n={rweb_video_screen_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!1,rweb_tipjar_consumption_enabled:!1,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,responsive_web_grok_analyze_button_fetch_trends_enabled:!1,responsive_web_grok_analyze_post_followups_enabled:!0,responsive_web_jetfuel_frame:!0,responsive_web_grok_share_attachment_enabled:!0,responsive_web_grok_annotations_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,content_disclosure_indicator_enabled:!0,content_disclosure_ai_generated_indicator_enabled:!0,responsive_web_grok_show_grok_translated_post:!1,responsive_web_grok_analysis_button_from_backend:!0,post_ctas_fetch_enabled:!1,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!1,responsive_web_grok_image_annotation_enabled:!0,responsive_web_grok_imagine_annotation_enabled:!0,responsive_web_grok_community_note_auto_translation_is_enabled:!1,responsive_web_enhance_cards_enabled:!1};function A(H,Z,J,$){const V={userId:Z,count:J,includePromotedContent:!1,withClientEventToken:!1,withBirdwatchNotes:!1,withVoice:!0,withV2Timeline:!0};if($)V.cursor=$;return`/i/api/graphql/${H}/Following?variables=${encodeURIComponent(JSON.stringify(V))}&features=${encodeURIComponent(JSON.stringify(n))}`}function h(H,Z){const J=JSON.stringify({screen_name:Z,withSafetyModeUserFields:!0}),$=JSON.stringify({hidden_profile_subscriptions_enabled:!0,rweb_tipjar_consumption_enabled:!0,responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,subscriptions_verification_info_is_identity_verified_enabled:!0,subscriptions_verification_info_verified_since_enabled:!0,highlights_tweets_tab_ui_enabled:!0,responsive_web_twitter_article_notes_tab_enabled:!0,subscriptions_feature_can_gift_premium:!0,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,responsive_web_graphql_timeline_navigation_enabled:!0});return`/i/api/graphql/${H}/UserByScreenName?variables=${encodeURIComponent(J)}&features=${encodeURIComponent($)}`}function N(H){if(!H||H.__typename!=="User")return null;const Z=H.core||{},J=H.legacy||{},$=Z.screen_name||J.screen_name||"";if(!$)throw new b("Malformed Twitter following user: missing screen_name");return{screen_name:$,name:Z.name||J.name||"",bio:J.description||H.profile_bio?.description||"",followers:J.followers_count||J.normal_followers_count||0}}function k(H){const Z=[];let J=null;const $=H?.data?.user?.result?.timeline_v2?.timeline?.instructions||H?.data?.user?.result?.timeline?.timeline?.instructions||[];for(const V of $)for(const W of V.entries||[]){const K=W.content;if(K?.entryType==="TimelineTimelineCursor"||K?.__typename==="TimelineTimelineCursor"){if(K.cursorType==="Bottom"||K.cursorType==="ShowMore")J=K.value;continue}if(W.entryId?.startsWith("cursor-bottom-")||W.entryId?.startsWith("cursor-showMore-")){J=K?.value||K?.itemContent?.value||J;continue}if(W.entryId?.startsWith("user-")){const O=N(K?.itemContent?.user_results?.result);if(O)Z.push(O)}}return{users:Z,nextCursor:J}}function q(H){return m(H)}U({site:"twitter",name:"following",access:"read",description:"Get accounts a Twitter/X user is following (defaults to the logged-in user when no user is given)",domain:"x.com",strategy:w.COOKIE,browser:!0,args:[{name:"user",positional:!0,type:"string",required:!1,help:"Twitter/X handle (with or without @). Omit to fetch the accounts the currently logged-in user follows."},{name:"limit",type:"int",default:50,help:"Maximum number of following rows to return (default 50). Must be a positive integer."}],columns:["screen_name","name","bio","followers"],func:async(H,Z)=>{const J=Z.limit===void 0||Z.limit===null?50:Number(Z.limit);if(!Number.isInteger(J)||J<=0)throw new v("twitter following --limit must be a positive integer","Example: opencli twitter following @elonmusk --limit 200");const $=String(Z.user??"").trim();let V=q($);if($&&!V)throw new v("twitter following user must be a valid Twitter/X handle","Example: opencli twitter following @elonmusk --limit 200");const K=(await H.getCookies({url:"https://x.com"})).find((X)=>X.name==="ct0")?.value||null;if(!K)throw new j("x.com","Not logged into x.com (no ct0 cookie)");if(!V){await H.goto("https://x.com/home");await H.wait({selector:'[data-testid="primaryColumn"]'});const X=B(await H.evaluate(`() => {
|
|
2
|
+
const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
|
|
3
|
+
return link ? link.getAttribute('href') : null;
|
|
4
|
+
}`));if(!X||typeof X!=="string")throw new j("x.com","Could not detect logged-in user. Are you logged in?");V=q(X);if(!V)throw new j("x.com","Could not detect logged-in user. Are you logged in?")}if(!V)throw new v("twitter following user cannot be empty","Example: opencli twitter following @elonmusk --limit 200");const O=await x(H,"Following",c),I=await x(H,"UserByScreenName",i),L={Authorization:`Bearer ${decodeURIComponent(g)}`,"X-Csrf-Token":K,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"},M=B(await H.evaluate(async(X,f)=>{const P=await fetch(X,{headers:f,credentials:"include"});if(!P.ok)return{error:P.status};return{userId:(await P.json()).data?.user?.result?.rest_id||null}},h(I,V),L));if(M?.error===401||M?.error===403)throw new j("x.com",`Twitter user lookup failed (HTTP ${M.error})`);if(M?.error)throw new b(`HTTP ${M.error}: Failed to resolve Twitter user @${V}`);const _=M?.userId||null;if(!_)throw new b(`Could not find user @${V}`);const G=[],S=new Set;let z=null,T=null;for(let X=0;X<l&&G.length<J;X++){const f=Math.min(50,J-G.length+10),P=A(O,_,f,z),D=B(await H.evaluate(async(Y,y)=>{const Q=await fetch(Y,{headers:y,credentials:"include"});return Q.ok?await Q.json():{error:Q.status}},P,L));if(D?.error){if(D.error===401||D.error===403)throw new j("x.com",`Twitter following request failed (HTTP ${D.error})`);throw new b(d("Following",D.error))}T=D;const{users:R,nextCursor:F}=k(D);for(const Y of R)if(!S.has(Y.screen_name)){S.add(Y.screen_name);G.push(Y)}if(!F||F===z)break;z=F}if(G.length===0){if(E(T))throw new C("twitter following",`No following data returned for @${V} (the target account may have set their following list to private)`);throw new C("twitter following",`No following accounts found for @${V}`)}return G.slice(0,J)}});export const __test__={sanitizeQueryId:p,buildFollowingUrl:A,buildUserByScreenNameUrl:h,extractUser:N,normalizeScreenName:q,parseFollowing:k};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import{CommandExecutionError as o}from"@jackwener/opencli/errors";import{cli as s,Strategy as l}from"@jackwener/opencli/registry";import{parseTweetUrl as n,buildTwitterArticleScopeSource as a}from"./shared.js";s({site:"twitter",name:"hide-reply",access:"write",description:"Hide a reply on your tweet (useful for hiding bot/spam replies)",domain:"x.com",strategy:l.UI,browser:!0,args:[{name:"url",type:"string",required:!0,positional:!0,help:"The URL of the reply tweet to hide"}],columns:["status","message"],func:async(e,i)=>{if(!e)throw new o("Browser session required for twitter hide-reply");const r=n(i.url);await e.goto(r.url);await e.wait({selector:'[data-testid="primaryColumn"]'});const t=await e.evaluate(`(async () => {
|
|
2
|
+
try {
|
|
3
|
+
${a(r.id)}
|
|
4
|
+
const visible = (el) => !!el && (el.offsetParent !== null || el.getClientRects().length > 0);
|
|
5
|
+
// Locate the article matching the requested status id, then find
|
|
6
|
+
// its More menu. Without article scoping we'd grab whatever the
|
|
7
|
+
// first "More" button on the page is — usually the parent tweet
|
|
8
|
+
// (silent: hide the wrong reply, or fail silently if the parent
|
|
9
|
+
// is not a reply you authored).
|
|
10
|
+
let attempts = 0;
|
|
11
|
+
let targetArticle = null;
|
|
12
|
+
let moreMenu = null;
|
|
13
|
+
|
|
14
|
+
while (attempts < 20) {
|
|
15
|
+
targetArticle = findTargetArticle();
|
|
16
|
+
if (targetArticle) {
|
|
17
|
+
const buttons = Array.from(targetArticle.querySelectorAll('button,[role="button"]'));
|
|
18
|
+
moreMenu = buttons.find((el) => visible(el) && (el.getAttribute('aria-label') || '').trim() === 'More');
|
|
19
|
+
if (moreMenu) break;
|
|
20
|
+
}
|
|
21
|
+
await new Promise(r => setTimeout(r, 500));
|
|
22
|
+
attempts++;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!targetArticle) {
|
|
26
|
+
return { ok: false, message: 'Could not find the requested reply article on this page.' };
|
|
27
|
+
}
|
|
28
|
+
if (!moreMenu) {
|
|
29
|
+
return { ok: false, message: 'Could not find the "More" menu on the requested reply. Are you logged in?' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
moreMenu.click();
|
|
33
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
34
|
+
|
|
35
|
+
// Look for the "Hide reply" menu item. Menu items render at the
|
|
36
|
+
// document root, not inside the article — scope is the open menu.
|
|
37
|
+
const items = document.querySelectorAll('[role="menuitem"]');
|
|
38
|
+
let hideItem = null;
|
|
39
|
+
for (const item of items) {
|
|
40
|
+
if (item.textContent && item.textContent.includes('Hide reply')) {
|
|
41
|
+
hideItem = item;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!hideItem) {
|
|
47
|
+
return { ok: false, message: 'Could not find "Hide reply" option. This may not be a reply on your tweet.' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
hideItem.click();
|
|
51
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
52
|
+
|
|
53
|
+
return { ok: true, message: 'Reply successfully hidden.' };
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return { ok: false, message: e.toString() };
|
|
56
|
+
}
|
|
57
|
+
})()`);if(t.ok)await e.wait(2);return[{status:t.ok?"success":"failed",message:t.message}]}});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import{CommandExecutionError as F}from"@jackwener/opencli/errors";import{cli as G,Strategy as H}from"@jackwener/opencli/registry";import{parseTweetUrl as I,buildTwitterArticleScopeSource as J}from"./shared.js";G({site:"twitter",name:"like",access:"write",description:"Like a specific tweet",domain:"x.com",strategy:H.UI,browser:!0,args:[{name:"url",type:"string",required:!0,positional:!0,help:"The URL of the tweet to like"}],columns:["status","message"],func:async(v,D)=>{if(!v)throw new F("Browser session required for twitter like");const B=I(D.url);await v.goto(B.url);await v.wait({selector:'[data-testid="primaryColumn"]'});const z=await v.evaluate(`(async () => {
|
|
2
|
+
try {
|
|
3
|
+
${J(B.id)}
|
|
4
|
+
// Poll for the tweet to render. We scope state probes to the
|
|
5
|
+
// article matching the requested status id — on conversation
|
|
6
|
+
// pages multiple articles render and a bare querySelector would
|
|
7
|
+
// grab the first one (silent: like the wrong tweet).
|
|
8
|
+
let attempts = 0;
|
|
9
|
+
let likeBtn = null;
|
|
10
|
+
let unlikeBtn = null;
|
|
11
|
+
let targetArticle = null;
|
|
12
|
+
|
|
13
|
+
while (attempts < 20) {
|
|
14
|
+
targetArticle = findTargetArticle();
|
|
15
|
+
likeBtn = targetArticle?.querySelector('[data-testid="like"]') || null;
|
|
16
|
+
unlikeBtn = targetArticle?.querySelector('[data-testid="unlike"]') || null;
|
|
17
|
+
|
|
18
|
+
if (likeBtn || unlikeBtn) break;
|
|
19
|
+
|
|
20
|
+
await new Promise(r => setTimeout(r, 500));
|
|
21
|
+
attempts++;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check if it's already liked
|
|
25
|
+
if (unlikeBtn) {
|
|
26
|
+
return { ok: true, message: 'Tweet is already liked.' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!likeBtn) {
|
|
30
|
+
return { ok: false, message: 'Could not find the Like button on this tweet after waiting 10 seconds. Are you logged in?' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Click Like
|
|
34
|
+
likeBtn.click();
|
|
35
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
36
|
+
|
|
37
|
+
// Verify success by checking if the 'unlike' button reappeared
|
|
38
|
+
const verifyArticle = findTargetArticle() || targetArticle;
|
|
39
|
+
const verifyBtn = verifyArticle?.querySelector('[data-testid="unlike"]');
|
|
40
|
+
if (verifyBtn) {
|
|
41
|
+
return { ok: true, message: 'Tweet successfully liked.' };
|
|
42
|
+
} else {
|
|
43
|
+
return { ok: false, message: 'Like action was initiated but UI did not update as expected.' };
|
|
44
|
+
}
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return { ok: false, message: e.toString() };
|
|
47
|
+
}
|
|
48
|
+
})()`);if(z.ok)await v.wait(2);return[{status:z.ok?"success":"failed",message:z.message}]}});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import{cli as k,Strategy as I}from"@jackwener/opencli/registry";import{ArgumentError as R,AuthRequiredError as j,CommandExecutionError as v,EmptyResultError as q}from"@jackwener/opencli/errors";import{looksLikePrivateTwitterTimeline as T,normalizeTwitterScreenName as Q,resolveTwitterQueryId as L,sanitizeQueryId as E,extractMedia as y,unwrapBrowserResult as b,describeTwitterApiError as g}from"./shared.js";import{TWITTER_BEARER_TOKEN as p,applyTopByEngagement as m}from"./utils.js";const d="CDWHmpZeSdIJ3HGeRbNm0w",w="IGgvgiOx4QZndDHuD3x9TQ",c=100,l={rweb_video_screen_enabled:!1,profile_label_improvements_pcf_label_in_post_enabled:!0,responsive_web_profile_redirect_enabled:!1,rweb_tipjar_consumption_enabled:!1,verified_phone_label_enabled:!1,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_timeline_navigation_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,premium_content_api_read_enabled:!1,communities_web_enable_tweet_community_results_fetch:!0,c9s_tweet_anatomy_moderator_badge_enabled:!0,responsive_web_grok_analyze_button_fetch_trends_enabled:!1,responsive_web_grok_analyze_post_followups_enabled:!0,responsive_web_jetfuel_frame:!0,responsive_web_grok_share_attachment_enabled:!0,responsive_web_grok_annotations_enabled:!0,articles_preview_enabled:!0,responsive_web_edit_tweet_api_enabled:!0,graphql_is_translatable_rweb_tweet_is_translatable_enabled:!0,view_counts_everywhere_api_enabled:!0,longform_notetweets_consumption_enabled:!0,responsive_web_twitter_article_tweet_consumption_enabled:!0,tweet_awards_web_tipping_enabled:!1,content_disclosure_indicator_enabled:!0,content_disclosure_ai_generated_indicator_enabled:!0,responsive_web_grok_show_grok_translated_post:!1,responsive_web_grok_analysis_button_from_backend:!0,post_ctas_fetch_enabled:!1,freedom_of_speech_not_reach_fetch_enabled:!0,standardized_nudges_misinfo:!0,tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:!0,longform_notetweets_rich_text_read_enabled:!0,longform_notetweets_inline_media_enabled:!1,responsive_web_grok_image_annotation_enabled:!0,responsive_web_grok_imagine_annotation_enabled:!0,responsive_web_grok_community_note_auto_translation_is_enabled:!1,responsive_web_enhance_cards_enabled:!1};function B(J,$,H,Z){const V={userId:$,count:H,includePromotedContent:!1,withClientEventToken:!1,withBirdwatchNotes:!1,withVoice:!0};if(Z)V.cursor=Z;return`/i/api/graphql/${J}/Likes?variables=${encodeURIComponent(JSON.stringify(V))}&features=${encodeURIComponent(JSON.stringify(l))}`}function h(J,$){const H=JSON.stringify({screen_name:$,withSafetyModeUserFields:!0}),Z=JSON.stringify({hidden_profile_subscriptions_enabled:!0,rweb_tipjar_consumption_enabled:!0,responsive_web_graphql_exclude_directive_enabled:!0,verified_phone_label_enabled:!1,subscriptions_verification_info_is_identity_verified_enabled:!0,subscriptions_verification_info_verified_since_enabled:!0,highlights_tweets_tab_ui_enabled:!0,responsive_web_twitter_article_notes_tab_enabled:!0,subscriptions_feature_can_gift_premium:!0,creator_subscriptions_tweet_preview_api_enabled:!0,responsive_web_graphql_skip_user_profile_image_extensions_enabled:!1,responsive_web_graphql_timeline_navigation_enabled:!0});return`/i/api/graphql/${J}/UserByScreenName?variables=${encodeURIComponent(H)}&features=${encodeURIComponent(Z)}`}function C(J,$){if(!J)return null;const H=J.tweet||J,Z=H.legacy||{};if(!H.rest_id||$.has(H.rest_id))return null;$.add(H.rest_id);const V=H.core?.user_results?.result,G=V?.legacy?.screen_name||V?.core?.screen_name||"unknown",W=V?.legacy?.name||V?.core?.name||"",F=H.note_tweet?.note_tweet_results?.result?.text;return{id:H.rest_id,author:G,name:W,text:F||Z.full_text||"",likes:Z.favorite_count||0,retweets:Z.retweet_count||0,created_at:Z.created_at||"",url:`https://x.com/${G}/status/${H.rest_id}`,...y(Z)}}function S(J,$){const H=[];let Z=null;const V=J?.data?.user?.result?.timeline_v2?.timeline?.instructions||J?.data?.user?.result?.timeline?.timeline?.instructions||[];for(const G of V)for(const W of G.entries||[]){const F=W.content;if(F?.entryType==="TimelineTimelineCursor"||F?.__typename==="TimelineTimelineCursor"){if(F.cursorType==="Bottom"||F.cursorType==="ShowMore")Z=F.value;continue}if(W.entryId?.startsWith("cursor-bottom-")||W.entryId?.startsWith("cursor-showMore-")){Z=F?.value||F?.itemContent?.value||Z;continue}const O=C(F?.itemContent?.tweet_results?.result,$);if(O){H.push(O);continue}for(const z of F?.items||[]){const K=C(z.item?.itemContent?.tweet_results?.result,$);if(K)H.push(K)}}return{tweets:H,nextCursor:Z}}k({site:"twitter",name:"likes",access:"read",description:"Fetch liked tweets of a Twitter user (defaults to the logged-in user when no username is given)",domain:"x.com",strategy:I.COOKIE,browser:!0,args:[{name:"username",type:"string",positional:!0,help:"Twitter screen name (with or without @). Defaults to the logged-in user when omitted."},{name:"limit",type:"int",default:20,help:"Maximum number of liked tweets to return (default 20)."},{name:"top-by-engagement",type:"int",default:0,help:"When set to N>0, re-rank the liked tweets by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API's native (recency) ordering."}],columns:["id","author","name","text","likes","retweets","created_at","url","has_media","media_urls","media_posters"],func:async(J,$)=>{const H=$.limit||20,Z=String($.username??"").trim();let V=Q(Z);if(Z&&!V)throw new R("twitter likes username must be a valid Twitter/X handle","Example: opencli twitter likes @jack --limit 20");const W=(await J.getCookies({url:"https://x.com"})).find((X)=>X.name==="ct0")?.value||null;if(!W)throw new j("x.com","Not logged into x.com (no ct0 cookie)");if(!V){await J.goto("https://x.com/home");await J.wait({selector:'[data-testid="primaryColumn"]'});const X=b(await J.evaluate(`() => {
|
|
2
|
+
const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
|
|
3
|
+
return link ? link.getAttribute('href') : null;
|
|
4
|
+
}`));if(!X||typeof X!=="string")throw new j("x.com","Could not detect logged-in user. Are you logged in?");V=Q(X);if(!V)throw new j("x.com","Could not detect logged-in user. Are you logged in?")}const F=await L(J,"Likes",d),O=await L(J,"UserByScreenName",w),z=JSON.stringify({Authorization:`Bearer ${decodeURIComponent(p)}`,"X-Csrf-Token":W,"X-Twitter-Auth-Type":"OAuth2Session","X-Twitter-Active-User":"yes"}),K=b(await J.evaluate(`async () => {
|
|
5
|
+
const screenName = ${JSON.stringify(V)};
|
|
6
|
+
const url = ${JSON.stringify(h(O,V))};
|
|
7
|
+
const resp = await fetch(url, { headers: ${z}, credentials: 'include' });
|
|
8
|
+
if (!resp.ok) return null;
|
|
9
|
+
const d = await resp.json();
|
|
10
|
+
return d.data?.user?.result?.rest_id || null;
|
|
11
|
+
}`));if(!K)throw new v(`Could not find user @${V}`);const D=[],_=new Set;let P=null,f=null;for(let X=0;X<c&&D.length<H;X++){const U=Math.min(100,H-D.length+10),x=B(F,K,U,P),M=b(await J.evaluate(`async () => {
|
|
12
|
+
const r = await fetch("${x}", { headers: ${z}, credentials: 'include' });
|
|
13
|
+
return r.ok ? await r.json() : { error: r.status };
|
|
14
|
+
}`));if(M?.error){if(D.length===0)throw new v(g("Likes",M.error));break}f=M;const{tweets:N,nextCursor:Y}=S(M,_);D.push(...N);if(!Y||Y===P)break;P=Y}if(D.length===0){if(T(f))throw new q("twitter likes",`No likes returned for @${V} (Likes are private by default on X; only the account owner can view their own likes)`);throw new q("twitter likes",`No likes found for @${V}`)}const A=D.slice(0,H);return m(A,$["top-by-engagement"])}});export const __test__={sanitizeQueryId:E,buildLikesUrl:B,buildUserByScreenNameUrl:h,parseLikes:S};
|