@jackwener/opencli 1.7.11 → 1.7.13
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/README.md +8 -7
- package/README.zh-CN.md +11 -9
- package/cli-manifest.json +12952 -6208
- package/clis/1688/assets.js +1 -0
- package/clis/1688/download.js +1 -0
- package/clis/1688/item.js +1 -0
- package/clis/1688/search.js +2 -1
- package/clis/1688/store.js +1 -0
- package/clis/1point3acres/digest.js +35 -0
- package/clis/1point3acres/forum.js +51 -0
- package/clis/1point3acres/forums.js +44 -0
- package/clis/1point3acres/hot.js +35 -0
- package/clis/1point3acres/latest.js +35 -0
- package/clis/1point3acres/notifications.js +64 -0
- package/clis/1point3acres/search.js +71 -0
- package/clis/1point3acres/thread.js +117 -0
- package/clis/1point3acres/user.js +77 -0
- package/clis/1point3acres/utils.js +247 -0
- package/clis/36kr/article.js +1 -0
- package/clis/36kr/hot.js +1 -0
- package/clis/36kr/news.js +1 -0
- package/clis/36kr/search.js +1 -0
- package/clis/51job/company.js +1 -0
- package/clis/51job/detail.js +1 -0
- package/clis/51job/hot.js +1 -0
- package/clis/51job/search.js +1 -0
- package/clis/_shared/desktop-commands.js +4 -0
- package/clis/aibase/news.js +110 -0
- package/clis/aibase/news.test.js +59 -0
- package/clis/amazon/bestsellers.js +1 -0
- package/clis/amazon/discussion.js +1 -0
- package/clis/amazon/discussion.test.js +1 -28
- package/clis/amazon/movers-shakers.js +1 -0
- package/clis/amazon/new-releases.js +1 -0
- package/clis/amazon/offer.js +1 -0
- package/clis/amazon/product.js +1 -0
- package/clis/amazon/rankings.js +1 -0
- package/clis/amazon/search.js +1 -0
- package/clis/antigravity/dump.js +1 -0
- package/clis/antigravity/extract-code.js +1 -0
- package/clis/antigravity/model.js +1 -0
- package/clis/antigravity/new.js +1 -0
- package/clis/antigravity/read.js +1 -0
- package/clis/antigravity/send.js +1 -0
- package/clis/antigravity/status.js +1 -0
- package/clis/antigravity/watch.js +4 -2
- 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/arxiv/arxiv.test.js +112 -0
- package/clis/arxiv/author.js +44 -0
- package/clis/arxiv/paper.js +4 -3
- package/clis/arxiv/recent.js +33 -0
- package/clis/arxiv/search.js +19 -7
- package/clis/arxiv/utils.js +68 -5
- package/clis/baidu-scholar/search.js +1 -1
- package/clis/band/bands.js +1 -0
- package/clis/band/mentions.js +1 -0
- package/clis/band/post.js +1 -0
- package/clis/band/posts.js +1 -0
- package/clis/barchart/flow.js +1 -0
- package/clis/barchart/greeks.js +1 -0
- package/clis/barchart/options.js +1 -0
- package/clis/barchart/quote.js +1 -0
- package/clis/bbc/news.js +1 -0
- package/clis/bbc/topic.js +57 -0
- package/clis/bbc/utils.js +79 -0
- package/clis/bilibili/comments.js +1 -0
- package/clis/bilibili/download.js +1 -0
- package/clis/bilibili/dynamic.js +1 -0
- package/clis/bilibili/favorite.js +1 -0
- package/clis/bilibili/feed.js +2 -0
- package/clis/bilibili/following.js +1 -0
- package/clis/bilibili/history.js +1 -0
- package/clis/bilibili/hot.js +6 -1
- package/clis/bilibili/hot.test.js +17 -0
- package/clis/bilibili/me.js +1 -1
- package/clis/bilibili/ranking.js +1 -0
- package/clis/bilibili/search.js +1 -1
- package/clis/bilibili/subtitle.js +1 -0
- package/clis/bilibili/user-videos.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 +1 -0
- package/clis/bloomberg/economics.js +1 -0
- package/clis/bloomberg/feeds.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 +1 -0
- package/clis/bloomberg/opinions.js +1 -0
- package/clis/bloomberg/politics.js +1 -0
- package/clis/bloomberg/tech.js +1 -0
- package/clis/bluesky/feeds.js +1 -0
- package/clis/bluesky/followers.js +1 -0
- package/clis/bluesky/following.js +1 -0
- package/clis/bluesky/profile.js +1 -0
- package/clis/bluesky/search.js +1 -0
- package/clis/bluesky/starter-packs.js +1 -0
- package/clis/bluesky/thread.js +1 -0
- package/clis/bluesky/trending.js +1 -0
- package/clis/bluesky/user.js +3 -1
- 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 +1 -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 +1 -0
- package/clis/boss/search.js +1 -0
- package/clis/boss/send.js +1 -0
- package/clis/boss/stats.js +1 -0
- package/clis/chaoxing/assignments.js +2 -1
- package/clis/chaoxing/exams.js +2 -1
- package/clis/chatgpt/ask.js +57 -0
- package/clis/chatgpt/commands.test.js +45 -0
- package/clis/chatgpt/detail.js +46 -0
- package/clis/chatgpt/history.js +39 -0
- package/clis/chatgpt/image.js +13 -11
- package/clis/chatgpt/image.test.js +23 -0
- package/clis/chatgpt/new.js +25 -0
- package/clis/chatgpt/read.js +43 -0
- package/clis/chatgpt/send.js +46 -0
- package/clis/chatgpt/status.js +29 -0
- package/clis/chatgpt/utils.js +294 -4
- package/clis/chatgpt/utils.test.js +13 -0
- package/clis/chatgpt-app/ask.js +7 -3
- 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 +17 -43
- package/clis/chatwise/composer.test.js +186 -0
- package/clis/chatwise/export.js +1 -0
- package/clis/chatwise/history.js +1 -0
- package/clis/chatwise/model.js +1 -0
- package/clis/chatwise/read.js +1 -0
- package/clis/chatwise/send.js +3 -24
- package/clis/chatwise/utils.js +143 -0
- package/clis/claude/ask.js +2 -1
- package/clis/claude/detail.js +2 -0
- package/clis/claude/history.js +2 -0
- package/clis/claude/new.js +2 -0
- package/clis/claude/read.js +2 -0
- package/clis/claude/send.js +2 -0
- package/clis/claude/status.js +2 -0
- package/clis/cnki/search.js +1 -0
- package/clis/codex/ask.js +16 -9
- package/clis/codex/export.js +1 -0
- package/clis/codex/extract-diff.js +1 -0
- package/clis/codex/history.js +17 -33
- package/clis/codex/model.js +1 -0
- package/clis/codex/projects.js +28 -0
- package/clis/codex/read.js +11 -4
- package/clis/codex/send.js +11 -3
- package/clis/codex/sidebar.js +356 -0
- package/clis/codex/sidebar.test.js +329 -0
- package/clis/coingecko/categories.js +75 -0
- package/clis/coingecko/coin.js +107 -0
- package/clis/coingecko/coingecko.test.js +109 -0
- package/clis/coingecko/derivatives.js +84 -0
- package/clis/coingecko/exchanges.js +74 -0
- package/clis/coingecko/global.js +71 -0
- package/clis/coingecko/top.js +64 -0
- package/clis/coingecko/trending.js +55 -0
- package/clis/coupang/add-to-cart.js +22 -13
- package/clis/coupang/coupang.test.js +159 -0
- package/clis/coupang/product.js +257 -0
- package/clis/coupang/search.js +39 -16
- package/clis/coupang/utils.js +55 -1
- package/clis/crates/crate.js +62 -0
- package/clis/crates/search.js +44 -0
- package/clis/crates/utils.js +72 -0
- package/clis/ctrip/ctrip.test.js +234 -0
- package/clis/ctrip/hotel-suggest.js +45 -0
- package/clis/ctrip/search.js +23 -68
- package/clis/ctrip/utils.js +175 -0
- package/clis/cursor/ask.js +7 -3
- package/clis/cursor/composer.js +1 -0
- package/clis/cursor/export.js +1 -0
- package/clis/cursor/extract-code.js +1 -0
- package/clis/cursor/history.js +1 -0
- package/clis/cursor/model.js +1 -0
- package/clis/cursor/read.js +1 -0
- package/clis/cursor/send.js +1 -0
- package/clis/dblp/author.js +133 -0
- package/clis/dblp/dblp.test.js +397 -0
- package/clis/dblp/paper.js +40 -0
- package/clis/dblp/search.js +45 -0
- package/clis/dblp/utils.js +290 -0
- package/clis/dblp/venue.js +64 -0
- package/clis/deepseek/ask.js +13 -7
- package/clis/deepseek/ask.test.js +13 -13
- package/clis/deepseek/detail.js +38 -0
- package/clis/deepseek/detail.test.js +81 -0
- package/clis/deepseek/history.js +2 -0
- package/clis/deepseek/new.js +2 -0
- package/clis/deepseek/read.js +2 -0
- package/clis/deepseek/send.js +140 -0
- package/clis/deepseek/send.test.js +107 -0
- package/clis/deepseek/status.js +2 -0
- package/clis/deepseek/utils.js +66 -0
- package/clis/deepseek/utils.test.js +107 -1
- package/clis/defillama/defillama.test.js +99 -0
- package/clis/defillama/protocol.js +84 -0
- package/clis/defillama/protocols.js +55 -0
- package/clis/defillama/utils.js +99 -0
- package/clis/devto/devto.test.js +236 -0
- package/clis/devto/latest.js +74 -0
- package/clis/devto/read.js +103 -0
- package/clis/devto/tag.js +5 -1
- package/clis/devto/top.js +5 -1
- package/clis/devto/user.js +5 -1
- package/clis/dianping/__fixtures__/search.html +168 -0
- package/clis/dianping/__fixtures__/shop.html +6 -0
- package/clis/dianping/dianping.test.js +424 -0
- package/clis/dianping/search.js +154 -0
- package/clis/dianping/shop.js +173 -0
- package/clis/dianping/utils.js +157 -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 +1 -0
- package/clis/discord-app/members.js +1 -0
- package/clis/discord-app/read.js +1 -0
- package/clis/discord-app/search.js +1 -0
- package/clis/discord-app/send.js +1 -0
- package/clis/discord-app/servers.js +1 -0
- package/clis/discord-app/status.js +1 -0
- package/clis/dockerhub/image.js +52 -0
- package/clis/dockerhub/search.js +47 -0
- package/clis/dockerhub/utils.js +100 -0
- package/clis/douban/book-hot.js +1 -0
- package/clis/douban/download.js +1 -0
- package/clis/douban/marks.js +1 -0
- package/clis/douban/movie-hot.js +2 -1
- package/clis/douban/movie-hot.test.js +14 -0
- package/clis/douban/photos.js +2 -1
- package/clis/douban/reviews.js +1 -0
- package/clis/douban/search.js +1 -0
- package/clis/douban/subject.js +1 -0
- package/clis/douban/top250.js +1 -0
- package/clis/douban/utils.js +11 -13
- package/clis/douban/utils.test.js +79 -0
- package/clis/doubao/ask.js +8 -3
- package/clis/doubao/detail.js +2 -0
- package/clis/doubao/history.js +2 -0
- package/clis/doubao/meeting-summary.js +2 -0
- package/clis/doubao/meeting-transcript.js +2 -0
- package/clis/doubao/new.js +2 -0
- package/clis/doubao/read.js +2 -0
- package/clis/doubao/send.js +2 -0
- package/clis/doubao/status.js +2 -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/douyin/activities.js +1 -0
- package/clis/douyin/collections.js +1 -0
- package/clis/douyin/delete.js +1 -0
- package/clis/douyin/draft.js +1 -0
- package/clis/douyin/draft.test.js +1 -30
- 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.js +1 -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/eastmoney/announcement.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 +1 -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/endoflife/endoflife.test.js +51 -0
- package/clis/endoflife/product.js +55 -0
- package/clis/endoflife/utils.js +89 -0
- package/clis/facebook/__fixtures__/notifications-page.html +13 -0
- package/clis/facebook/add-friend.js +1 -0
- package/clis/facebook/events.js +1 -0
- package/clis/facebook/feed.js +1 -0
- package/clis/facebook/friends.js +1 -0
- package/clis/facebook/groups.js +1 -0
- package/clis/facebook/join-group.js +1 -0
- package/clis/facebook/marketplace-inbox.js +1 -0
- package/clis/facebook/marketplace-listings.js +1 -0
- package/clis/facebook/memories.js +1 -0
- package/clis/facebook/notifications.js +327 -30
- package/clis/facebook/notifications.test.js +458 -0
- package/clis/facebook/profile.js +1 -0
- package/clis/facebook/search.js +1 -0
- package/clis/flathub/app.js +71 -0
- package/clis/flathub/flathub.test.js +90 -0
- package/clis/flathub/search.js +80 -0
- package/clis/flathub/utils.js +114 -0
- package/clis/gemini/ask.js +8 -3
- package/clis/gemini/ask.test.js +2 -2
- package/clis/gemini/deep-research-result.js +7 -2
- package/clis/gemini/deep-research-result.test.js +15 -14
- package/clis/gemini/deep-research.js +9 -4
- package/clis/gemini/deep-research.test.js +15 -18
- package/clis/gemini/image.js +8 -2
- package/clis/gemini/new.js +2 -0
- package/clis/gemini/utils.js +0 -4
- package/clis/gitee/search.js +1 -0
- package/clis/gitee/trending.js +1 -0
- package/clis/gitee/user.js +1 -0
- package/clis/google/news.js +1 -0
- package/clis/google/search.js +1 -0
- package/clis/google/suggest.js +1 -0
- package/clis/google/trends.js +1 -0
- package/clis/google-scholar/cite.js +1 -1
- package/clis/google-scholar/profile.js +1 -1
- package/clis/google-scholar/search.js +1 -1
- package/clis/goproxy/goproxy.test.js +103 -0
- package/clis/goproxy/module.js +47 -0
- package/clis/goproxy/utils.js +165 -0
- package/clis/goproxy/versions.js +59 -0
- package/clis/gov-law/recent.js +1 -1
- package/clis/gov-law/search.js +1 -1
- package/clis/gov-policy/__fixtures__/recent.html +16 -0
- package/clis/gov-policy/__fixtures__/search.html +41 -0
- package/clis/gov-policy/gov-policy.test.js +224 -0
- package/clis/gov-policy/recent.js +67 -24
- package/clis/gov-policy/search.js +66 -23
- package/clis/gov-policy/utils.js +54 -0
- package/clis/grok/ask.js +50 -265
- package/clis/grok/ask.test.js +21 -46
- package/clis/grok/detail.js +60 -0
- package/clis/grok/history.js +48 -0
- package/clis/grok/{image.ts → image.js} +57 -70
- package/clis/grok/image.test.ts +20 -0
- package/clis/grok/new.js +20 -0
- package/clis/grok/read.js +39 -0
- package/clis/grok/send.js +50 -0
- package/clis/grok/status.js +41 -0
- package/clis/grok/utils.js +326 -0
- package/clis/grok/utils.test.js +103 -0
- package/clis/hackernews/ask.js +3 -1
- package/clis/hackernews/best.js +3 -1
- package/clis/hackernews/hackernews.test.js +132 -0
- package/clis/hackernews/jobs.js +3 -1
- package/clis/hackernews/new.js +3 -1
- package/clis/hackernews/read.js +188 -0
- package/clis/hackernews/search.js +3 -1
- package/clis/hackernews/show.js +3 -1
- package/clis/hackernews/top.js +3 -1
- package/clis/hackernews/user.js +1 -0
- package/clis/hf/datasets.js +88 -0
- package/clis/hf/hf.test.js +16 -0
- package/clis/hf/models.js +91 -0
- package/clis/hf/paper.js +79 -0
- package/clis/hf/spaces.js +101 -0
- package/clis/hf/top.js +2 -0
- package/clis/homebrew/cask.js +39 -0
- package/clis/homebrew/formula.js +41 -0
- package/clis/homebrew/popular.js +54 -0
- package/clis/homebrew/utils.js +100 -0
- package/clis/hupu/__fixtures__/hot-home.html +64 -0
- package/clis/hupu/detail.js +1 -1
- package/clis/hupu/hot.js +157 -34
- package/clis/hupu/hot.test.js +224 -0
- package/clis/hupu/like.js +1 -0
- package/clis/hupu/mentions.js +2 -1
- package/clis/hupu/reply.js +1 -0
- package/clis/hupu/search.js +3 -2
- package/clis/hupu/unlike.js +1 -0
- package/clis/imdb/person.js +1 -0
- package/clis/imdb/reviews.js +1 -0
- package/clis/imdb/search.js +1 -0
- package/clis/imdb/title.js +1 -0
- package/clis/imdb/top.js +1 -0
- package/clis/imdb/trending.js +1 -0
- package/clis/indeed/indeed.test.js +375 -0
- package/clis/indeed/job.js +86 -0
- package/clis/indeed/search.js +110 -0
- package/clis/indeed/utils.js +152 -0
- package/clis/instagram/collection-create.js +1 -0
- package/clis/instagram/collection-delete.js +1 -0
- package/clis/instagram/comment.js +1 -0
- package/clis/instagram/download.js +1 -0
- package/clis/instagram/explore.js +1 -0
- package/clis/instagram/follow.js +1 -0
- package/clis/instagram/followers.js +1 -0
- package/clis/instagram/following.js +1 -0
- package/clis/instagram/like.js +1 -0
- package/clis/instagram/note.js +2 -1
- package/clis/instagram/note.test.js +1 -29
- package/clis/instagram/post.js +2 -1
- package/clis/instagram/post.test.js +1 -1
- package/clis/instagram/profile.js +1 -0
- package/clis/instagram/reel.js +2 -1
- package/clis/instagram/save.js +1 -0
- package/clis/instagram/saved.js +1 -0
- package/clis/instagram/search.js +1 -0
- package/clis/instagram/story.js +2 -1
- package/clis/instagram/story.test.js +1 -34
- package/clis/instagram/unfollow.js +1 -0
- package/clis/instagram/unlike.js +1 -0
- package/clis/instagram/unsave.js +1 -0
- package/clis/instagram/user.js +1 -0
- package/clis/jd/add-cart.js +1 -0
- package/clis/jd/cart.js +1 -0
- package/clis/jd/commands.test.js +1 -24
- package/clis/jd/detail.js +1 -0
- package/clis/jd/item.js +1 -0
- package/clis/jd/reviews.js +1 -0
- package/clis/jd/search.js +1 -0
- package/clis/jianyu/detail.js +1 -0
- package/clis/jianyu/search.js +1 -0
- package/clis/jike/comment.js +1 -0
- package/clis/jike/create.js +1 -0
- package/clis/jike/feed.js +3 -1
- package/clis/jike/like.js +1 -0
- package/clis/jike/notifications.js +1 -0
- package/clis/jike/post.js +1 -0
- package/clis/jike/repost.js +1 -0
- package/clis/jike/search.js +3 -1
- package/clis/jike/topic.js +1 -0
- package/clis/jike/user.js +3 -1
- package/clis/jimeng/generate.js +1 -0
- package/clis/jimeng/history.js +1 -0
- package/clis/jimeng/new.js +1 -0
- package/clis/jimeng/workspaces.js +1 -0
- package/clis/ke/chengjiao.js +1 -0
- package/clis/ke/ershoufang.js +1 -0
- package/clis/ke/xiaoqu.js +1 -0
- package/clis/ke/zufang.js +1 -0
- package/clis/lesswrong/comments.js +1 -0
- package/clis/lesswrong/curated.js +1 -0
- package/clis/lesswrong/frontpage.js +1 -0
- package/clis/lesswrong/new.js +1 -0
- package/clis/lesswrong/read.js +1 -0
- package/clis/lesswrong/sequences.js +1 -0
- package/clis/lesswrong/shortform.js +1 -0
- package/clis/lesswrong/tag.js +1 -0
- package/clis/lesswrong/tags.js +1 -0
- package/clis/lesswrong/top-month.js +1 -0
- package/clis/lesswrong/top-week.js +1 -0
- package/clis/lesswrong/top-year.js +1 -0
- package/clis/lesswrong/top.js +1 -0
- package/clis/lesswrong/user-posts.js +1 -0
- package/clis/lesswrong/user.js +1 -0
- package/clis/lichess/lichess.test.js +85 -0
- package/clis/lichess/top.js +46 -0
- package/clis/lichess/user.js +91 -0
- package/clis/lichess/utils.js +97 -0
- package/clis/linkedin/search.js +108 -10
- package/clis/linkedin/search.test.js +222 -0
- package/clis/linkedin/timeline.js +1 -0
- package/clis/linux-do/categories.js +1 -0
- package/clis/linux-do/feed.js +3 -5
- package/clis/linux-do/feed.test.js +35 -0
- package/clis/linux-do/search.js +1 -0
- package/clis/linux-do/tags.js +2 -1
- package/clis/linux-do/topic-content.js +1 -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 +4 -1
- package/clis/lobsters/domain.js +92 -0
- package/clis/lobsters/hot.js +4 -1
- package/clis/lobsters/lobsters.test.js +169 -0
- package/clis/lobsters/newest.js +4 -1
- package/clis/lobsters/read.js +196 -0
- package/clis/lobsters/tag.js +4 -1
- package/clis/maimai/search-talents.js +1 -0
- package/clis/maven/artifact.js +49 -0
- package/clis/maven/search.js +51 -0
- package/clis/maven/utils.js +110 -0
- package/clis/mdn/search.js +97 -0
- package/clis/medium/feed.js +1 -0
- package/clis/medium/search.js +1 -0
- package/clis/medium/tag.js +135 -0
- package/clis/medium/user.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/notebooklm/current.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/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/notion/export.js +1 -0
- package/clis/notion/favorites.js +1 -0
- package/clis/notion/new.js +1 -0
- package/clis/notion/read.js +1 -0
- package/clis/notion/search.js +1 -0
- package/clis/notion/sidebar.js +1 -0
- package/clis/notion/status.js +1 -0
- package/clis/notion/write.js +1 -0
- package/clis/nowcoder/companies.js +1 -0
- package/clis/nowcoder/creators.js +1 -0
- package/clis/nowcoder/detail.js +1 -0
- package/clis/nowcoder/experience.js +1 -0
- package/clis/nowcoder/hot.js +1 -0
- package/clis/nowcoder/jobs.js +1 -0
- package/clis/nowcoder/notifications.js +1 -0
- package/clis/nowcoder/papers.js +1 -0
- package/clis/nowcoder/practice.js +1 -0
- package/clis/nowcoder/recommend.js +1 -0
- package/clis/nowcoder/referral.js +1 -0
- package/clis/nowcoder/salary.js +1 -0
- package/clis/nowcoder/search.js +1 -0
- package/clis/nowcoder/suggest.js +1 -0
- package/clis/nowcoder/topics.js +1 -0
- package/clis/nowcoder/trending.js +1 -0
- package/clis/npm/downloads.js +59 -0
- package/clis/npm/package.js +70 -0
- package/clis/npm/search.js +49 -0
- package/clis/npm/utils.js +76 -0
- package/clis/nuget/nuget.test.js +111 -0
- package/clis/nuget/package.js +101 -0
- package/clis/nuget/search.js +69 -0
- package/clis/nuget/utils.js +87 -0
- package/clis/nvd/cve.js +121 -0
- package/clis/oeis/oeis.test.js +88 -0
- package/clis/oeis/search.js +63 -0
- package/clis/oeis/sequence.js +71 -0
- package/clis/oeis/utils.js +88 -0
- package/clis/ones/login.js +1 -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/task.js +1 -0
- package/clis/ones/tasks.js +1 -0
- package/clis/ones/token-info.js +1 -0
- package/clis/ones/worklog.js +1 -0
- package/clis/openalex/search.js +69 -0
- package/clis/openalex/utils.js +160 -0
- package/clis/openalex/work.js +65 -0
- package/clis/openfda/drug-label.js +74 -0
- package/clis/openfda/food-recall.js +65 -0
- package/clis/openfda/openfda.test.js +114 -0
- package/clis/openfda/utils.js +67 -0
- package/clis/openreview/openreview.test.js +345 -0
- package/clis/openreview/paper.js +43 -0
- package/clis/openreview/reviews.js +131 -0
- package/clis/openreview/search.js +46 -0
- package/clis/openreview/utils.js +158 -0
- package/clis/openreview/venue.js +63 -0
- package/clis/osv/osv.test.js +97 -0
- package/clis/osv/query.js +72 -0
- package/clis/osv/utils.js +169 -0
- package/clis/osv/vulnerability.js +54 -0
- package/clis/packagist/package.js +49 -0
- package/clis/packagist/search.js +43 -0
- package/clis/packagist/utils.js +113 -0
- package/clis/paperreview/feedback.js +2 -1
- package/clis/paperreview/review.js +2 -1
- package/clis/paperreview/submit.js +2 -1
- package/clis/pixiv/detail.js +1 -0
- package/clis/pixiv/download.js +1 -0
- package/clis/pixiv/download.test.js +1 -1
- package/clis/pixiv/illusts.js +2 -1
- package/clis/pixiv/illusts.test.js +1 -1
- package/clis/pixiv/ranking.js +2 -1
- package/clis/pixiv/search.js +2 -1
- package/clis/pixiv/search.test.js +1 -1
- package/clis/pixiv/user.js +2 -0
- package/clis/powerchina/search.js +1 -0
- package/clis/producthunt/browse.js +1 -0
- package/clis/producthunt/hot.js +1 -0
- package/clis/producthunt/posts.js +1 -0
- package/clis/producthunt/today.js +1 -0
- package/clis/pubmed/article.js +50 -0
- package/clis/pubmed/author.js +64 -0
- package/clis/pubmed/citations.js +36 -0
- package/clis/pubmed/pubmed.test.js +276 -0
- package/clis/pubmed/related.js +45 -0
- package/clis/pubmed/search.js +75 -0
- package/clis/pubmed/utils.js +309 -0
- package/clis/pypi/downloads.js +66 -0
- package/clis/pypi/package.js +79 -0
- package/clis/pypi/utils.js +55 -0
- package/clis/quark/ls.js +1 -0
- package/clis/quark/mkdir.js +1 -0
- package/clis/quark/mv.js +2 -1
- package/clis/quark/rename.js +1 -0
- package/clis/quark/rm.js +1 -0
- package/clis/quark/save.js +2 -1
- package/clis/quark/share-tree.js +1 -0
- package/clis/qwen/ask.js +85 -0
- package/clis/qwen/detail.js +62 -0
- package/clis/qwen/history.js +61 -0
- package/clis/qwen/image.js +179 -0
- package/clis/qwen/new.js +23 -0
- package/clis/qwen/read.js +41 -0
- package/clis/qwen/send.js +55 -0
- package/clis/qwen/status.js +37 -0
- package/clis/qwen/utils.js +409 -0
- package/clis/qwen/utils.test.js +45 -0
- package/clis/reddit/comment.js +1 -0
- package/clis/reddit/frontpage.js +1 -0
- package/clis/reddit/hot.js +6 -1
- package/clis/reddit/hot.test.js +18 -0
- package/clis/reddit/popular.js +1 -0
- package/clis/reddit/read.js +1 -0
- package/clis/reddit/save.js +1 -0
- package/clis/reddit/saved.js +1 -0
- package/clis/reddit/search.js +1 -0
- package/clis/reddit/subreddit.js +1 -0
- package/clis/reddit/subscribe.js +1 -0
- package/clis/reddit/upvote.js +1 -0
- package/clis/reddit/upvoted.js +1 -0
- package/clis/reddit/user-comments.js +1 -0
- package/clis/reddit/user-posts.js +1 -0
- package/clis/reddit/user.js +1 -0
- package/clis/rest-countries/country.js +65 -0
- package/clis/rest-countries/region.js +64 -0
- package/clis/rest-countries/rest-countries.test.js +83 -0
- package/clis/rest-countries/utils.js +126 -0
- package/clis/reuters/article-detail.js +53 -0
- package/clis/reuters/reuters.test.js +299 -0
- package/clis/reuters/search.js +46 -34
- package/clis/reuters/utils.js +159 -0
- package/clis/rfc/rfc.js +52 -0
- package/clis/rfc/rfc.test.js +74 -0
- package/clis/rfc/utils.js +72 -0
- package/clis/rubygems/gem.js +42 -0
- package/clis/rubygems/search.js +47 -0
- package/clis/rubygems/utils.js +86 -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/sinafinance/news.js +1 -0
- package/clis/sinafinance/rolling-news.js +1 -0
- package/clis/sinafinance/stock-rank.js +1 -0
- package/clis/sinafinance/stock.js +1 -0
- package/clis/smzdm/search.js +1 -0
- package/clis/spotify/spotify.js +11 -0
- package/clis/stackoverflow/bounties.js +11 -3
- package/clis/stackoverflow/hot.js +10 -2
- package/clis/stackoverflow/read.js +314 -0
- package/clis/stackoverflow/related.js +66 -0
- package/clis/stackoverflow/search.js +10 -2
- package/clis/stackoverflow/stackoverflow.test.js +404 -0
- package/clis/stackoverflow/tag.js +60 -0
- package/clis/stackoverflow/unanswered.js +9 -2
- package/clis/stackoverflow/user.js +50 -0
- package/clis/stackoverflow/utils.js +118 -0
- package/clis/steam/app.js +67 -0
- package/clis/steam/search.js +58 -0
- package/clis/steam/steam.test.js +46 -0
- package/clis/steam/top-sellers.js +1 -0
- package/clis/steam/utils.js +107 -0
- package/clis/substack/feed.js +1 -0
- package/clis/substack/publication.js +1 -0
- package/clis/substack/search.js +1 -0
- package/clis/taobao/add-cart.js +1 -0
- package/clis/taobao/cart.js +1 -0
- package/clis/taobao/commands.test.js +1 -24
- package/clis/taobao/detail.js +1 -0
- package/clis/taobao/reviews.js +1 -0
- package/clis/taobao/search.js +1 -0
- package/clis/tdx/hot-rank.js +1 -0
- package/clis/test-utils.js +61 -0
- package/clis/ths/hot-rank.js +1 -0
- package/clis/tieba/hot.js +2 -2
- package/clis/tieba/posts.js +1 -0
- package/clis/tieba/read.js +1 -0
- package/clis/tieba/search.js +2 -1
- package/clis/tiktok/comment.js +128 -40
- package/clis/tiktok/creator-videos.js +270 -0
- package/clis/tiktok/creator-videos.test.js +113 -0
- package/clis/tiktok/explore.js +138 -29
- package/clis/tiktok/follow.js +116 -33
- package/clis/tiktok/following.js +157 -35
- package/clis/tiktok/friends.js +140 -37
- package/clis/tiktok/like.js +1 -0
- package/clis/tiktok/live.js +138 -41
- package/clis/tiktok/notifications.js +142 -38
- package/clis/tiktok/profile.js +1 -0
- package/clis/tiktok/refactor.test.js +389 -0
- package/clis/tiktok/save.js +1 -0
- package/clis/tiktok/search.js +1 -0
- package/clis/tiktok/unfollow.js +125 -38
- package/clis/tiktok/unlike.js +1 -0
- package/clis/tiktok/unsave.js +1 -0
- package/clis/tiktok/user.js +204 -29
- package/clis/tiktok/utils.js +505 -0
- package/clis/tiktok/write-refactor.test.js +370 -0
- package/clis/toutiao/articles.js +37 -62
- package/clis/toutiao/hot.js +63 -0
- package/clis/toutiao/toutiao.test.js +378 -0
- package/clis/toutiao/utils.js +161 -0
- package/clis/tvmaze/search.js +61 -0
- package/clis/tvmaze/show.js +60 -0
- package/clis/tvmaze/tvmaze.test.js +93 -0
- package/clis/tvmaze/utils.js +110 -0
- package/clis/twitter/accept.js +2 -1
- package/clis/twitter/article.js +1 -0
- package/clis/twitter/block.js +1 -0
- package/clis/twitter/bookmark.js +1 -0
- package/clis/twitter/bookmarks.js +2 -1
- package/clis/twitter/delete.js +1 -0
- package/clis/twitter/download.js +1 -0
- package/clis/twitter/follow.js +1 -0
- package/clis/twitter/followers.js +135 -69
- package/clis/twitter/following.js +1 -0
- package/clis/twitter/hide-reply.js +1 -0
- package/clis/twitter/like.js +1 -0
- package/clis/twitter/likes.js +2 -1
- package/clis/twitter/list-add.js +1 -0
- package/clis/twitter/list-remove.js +1 -0
- package/clis/twitter/list-tweets.js +1 -0
- package/clis/twitter/lists.js +1 -0
- package/clis/twitter/notifications.js +1 -0
- package/clis/twitter/post.js +1 -0
- package/clis/twitter/profile.js +1 -0
- package/clis/twitter/reply-dm.js +2 -1
- package/clis/twitter/reply.js +1 -0
- package/clis/twitter/reply.test.js +1 -29
- package/clis/twitter/search.js +1 -0
- package/clis/twitter/thread.js +1 -0
- package/clis/twitter/timeline.js +1 -0
- package/clis/twitter/trending.js +11 -12
- package/clis/twitter/trending.test.js +15 -0
- package/clis/twitter/tweets.js +2 -1
- package/clis/twitter/tweets.test.js +2 -2
- package/clis/twitter/unblock.js +1 -0
- package/clis/twitter/unbookmark.js +1 -0
- package/clis/twitter/unfollow.js +1 -0
- package/clis/uisdc/news.js +105 -0
- package/clis/uisdc/news.test.js +66 -0
- package/clis/uiverse/code.js +1 -0
- package/clis/uiverse/preview.js +1 -0
- package/clis/v2ex/daily.js +1 -0
- package/clis/v2ex/hot.js +1 -0
- package/clis/v2ex/latest.js +1 -0
- package/clis/v2ex/me.js +1 -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 +1 -0
- package/clis/v2ex/replies.js +1 -0
- package/clis/v2ex/topic.js +1 -0
- package/clis/v2ex/user.js +1 -0
- package/clis/wanfang/search.js +1 -1
- package/clis/web/read.js +48 -17
- package/clis/web/read.test.js +101 -1
- package/clis/weibo/comments.js +1 -0
- package/clis/weibo/favorites.js +1 -0
- package/clis/weibo/feed.js +3 -1
- package/clis/weibo/hot.js +1 -0
- package/clis/weibo/me.js +1 -0
- package/clis/weibo/post.js +1 -0
- package/clis/weibo/publish.js +1 -0
- package/clis/weibo/search.js +8 -2
- package/clis/weibo/user.js +1 -0
- package/clis/weixin/create-draft.js +2 -1
- package/clis/weixin/download.js +1 -0
- package/clis/weixin/drafts.js +2 -1
- package/clis/weixin/drafts.test.js +5 -1
- package/clis/weixin/search.js +157 -0
- package/clis/weixin/search.test.js +227 -0
- package/clis/weread/ai-outline.js +1 -0
- package/clis/weread/book.js +1 -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/wikidata/entity.js +60 -0
- package/clis/wikidata/search.js +50 -0
- package/clis/wikidata/utils.js +117 -0
- package/clis/wikidata/wikidata.test.js +83 -0
- package/clis/wikipedia/page.js +95 -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/wttr/current.js +63 -0
- package/clis/wttr/forecast.js +71 -0
- package/clis/wttr/utils.js +50 -0
- package/clis/wttr/wttr.test.js +84 -0
- package/clis/xianyu/chat.js +17 -4
- package/clis/xianyu/chat.test.js +64 -0
- package/clis/xianyu/item.js +1 -0
- package/clis/xianyu/publish.js +485 -0
- package/clis/xianyu/publish.test.js +220 -0
- package/clis/xianyu/search.js +1 -0
- package/clis/xiaoe/catalog.js +105 -39
- package/clis/xiaoe/content.js +165 -29
- package/clis/xiaoe/courses.js +86 -28
- package/clis/xiaoe/detail.js +1 -0
- package/clis/xiaoe/play-url.js +1 -0
- package/clis/xiaoe/xiaoe.test.js +486 -0
- package/clis/xiaohongshu/comments.js +1 -0
- package/clis/xiaohongshu/creator-note-detail.js +1 -0
- package/clis/xiaohongshu/creator-notes-summary.js +2 -1
- package/clis/xiaohongshu/creator-notes.js +1 -0
- package/clis/xiaohongshu/creator-profile.js +1 -0
- package/clis/xiaohongshu/creator-stats.js +1 -0
- package/clis/xiaohongshu/download.js +1 -0
- package/clis/xiaohongshu/feed.js +2 -1
- package/clis/xiaohongshu/note.js +1 -0
- package/clis/xiaohongshu/notifications.js +1 -0
- package/clis/xiaohongshu/publish.js +17 -3
- package/clis/xiaohongshu/publish.test.js +46 -1
- package/clis/xiaohongshu/search.js +1 -0
- package/clis/xiaohongshu/user.js +1 -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/xueqiu/comments.js +1 -0
- package/clis/xueqiu/earnings-date.js +1 -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/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/watchlist.js +1 -0
- package/clis/yahoo-finance/quote.js +1 -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 +1 -0
- package/clis/yollomi/upscale.js +1 -0
- package/clis/yollomi/video.js +1 -0
- package/clis/youtube/channel.js +1 -0
- package/clis/youtube/comments.js +1 -0
- package/clis/youtube/feed.js +8 -7
- package/clis/youtube/feed.test.js +131 -0
- package/clis/youtube/history.js +1 -0
- package/clis/youtube/like.js +1 -0
- package/clis/youtube/playlist.js +1 -0
- package/clis/youtube/search.js +1 -0
- package/clis/youtube/subscribe.js +1 -0
- package/clis/youtube/subscriptions.js +1 -0
- package/clis/youtube/transcript.js +14 -19
- package/clis/youtube/transcript.test.js +17 -0
- package/clis/youtube/unlike.js +1 -0
- package/clis/youtube/unsubscribe.js +1 -0
- package/clis/youtube/video.js +1 -0
- package/clis/youtube/watch-later.js +1 -0
- package/clis/yuanbao/ask.js +18 -66
- package/clis/yuanbao/ask.test.js +5 -5
- package/clis/yuanbao/detail.js +65 -0
- package/clis/yuanbao/history.js +51 -0
- package/clis/yuanbao/new.js +2 -0
- package/clis/yuanbao/read.js +38 -0
- package/clis/yuanbao/send.js +57 -0
- package/clis/yuanbao/shared.js +297 -5
- package/clis/yuanbao/shared.test.js +80 -0
- package/clis/yuanbao/status.js +44 -0
- package/clis/zhihu/answer.js +1 -0
- package/clis/zhihu/collection.js +1 -0
- package/clis/zhihu/collections.js +1 -0
- package/clis/zhihu/comment.js +1 -0
- package/clis/zhihu/download.js +1 -0
- package/clis/zhihu/favorite.js +1 -0
- package/clis/zhihu/follow.js +1 -0
- package/clis/zhihu/hot.js +1 -0
- package/clis/zhihu/like.js +1 -0
- package/clis/zhihu/question.js +1 -0
- package/clis/zhihu/search.js +1 -0
- package/clis/zlibrary/commands.test.js +1 -11
- package/clis/zlibrary/info.js +1 -0
- package/clis/zlibrary/search.js +1 -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/dist/src/adapter-source.test.js +1 -1
- package/dist/src/browser/analyze.test.js +2 -0
- package/dist/src/browser/base-page.d.ts +18 -0
- package/dist/src/browser/base-page.js +116 -4
- package/dist/src/browser/base-page.test.js +108 -0
- package/dist/src/browser/cdp.d.ts +1 -0
- package/dist/src/browser/cdp.js +57 -9
- package/dist/src/browser/cdp.test.js +3 -0
- package/dist/src/browser/daemon-client.d.ts +4 -0
- package/dist/src/browser/errors.js +1 -1
- package/dist/src/browser/page.d.ts +2 -1
- package/dist/src/browser/page.js +9 -1
- package/dist/src/browser/page.test.js +46 -0
- package/dist/src/browser/target-errors.d.ts +2 -1
- package/dist/src/browser/target-errors.js +1 -0
- package/dist/src/browser/target-resolver.d.ts +35 -3
- package/dist/src/browser/target-resolver.js +59 -10
- package/dist/src/browser/verify-fixture.d.ts +6 -1
- package/dist/src/browser/verify-fixture.js +87 -0
- package/dist/src/browser/verify-fixture.test.js +44 -1
- package/dist/src/build-manifest.js +12 -4
- package/dist/src/build-manifest.test.js +20 -20
- package/dist/src/capabilityRouting.d.ts +16 -1
- package/dist/src/capabilityRouting.js +24 -1
- package/dist/src/capabilityRouting.test.js +20 -2
- package/dist/src/cli.js +213 -12
- package/dist/src/cli.test.js +329 -0
- package/dist/src/commanderAdapter.d.ts +1 -1
- package/dist/src/commanderAdapter.js +17 -12
- package/dist/src/commanderAdapter.test.js +7 -7
- package/dist/src/convention-audit.d.ts +50 -0
- package/dist/src/convention-audit.js +546 -0
- package/dist/src/convention-audit.test.d.ts +1 -0
- package/dist/src/convention-audit.test.js +226 -0
- package/dist/src/discovery.js +4 -5
- package/dist/src/doctor.js +0 -1
- package/dist/src/doctor.test.js +1 -1
- package/dist/src/engine.test.js +10 -10
- package/dist/src/errors.js +1 -1
- package/dist/src/execution.d.ts +1 -1
- package/dist/src/execution.js +111 -27
- package/dist/src/execution.test.js +334 -25
- package/dist/src/help.d.ts +36 -0
- package/dist/src/help.js +171 -0
- package/dist/src/help.test.d.ts +1 -0
- package/dist/src/help.test.js +54 -0
- package/dist/src/main.js +27 -8
- package/dist/src/manifest-types.d.ts +7 -3
- package/dist/src/pipeline/executor.js +1 -1
- package/dist/src/pipeline/executor.test.js +8 -0
- package/dist/src/pipeline/registry.d.ts +9 -0
- package/dist/src/pipeline/registry.js +13 -1
- package/dist/src/pipeline/steps/browser.d.ts +1 -0
- package/dist/src/pipeline/steps/browser.js +10 -0
- package/dist/src/pipeline/steps/download.test.js +1 -0
- package/dist/src/plugin.test.js +26 -26
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/registry.d.ts +17 -11
- package/dist/src/registry.js +24 -6
- package/dist/src/registry.test.js +29 -22
- package/dist/src/runtime.d.ts +2 -1
- package/dist/src/runtime.js +1 -1
- package/dist/src/serialization.d.ts +6 -2
- package/dist/src/serialization.js +31 -7
- package/dist/src/serialization.test.js +41 -2
- package/dist/src/types.d.ts +19 -0
- package/dist/src/validate.js +15 -11
- package/dist/src/validate.test.d.ts +9 -0
- package/dist/src/validate.test.js +90 -0
- package/package.json +4 -1
- 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/fetch-adapters.js +1 -1
- package/scripts/silent-column-drop-baseline.json +962 -0
- package/scripts/typed-error-lint-baseline.json +1514 -0
- package/clis/ctrip/search.test.js +0 -64
- package/clis/gov-policy/commands.test.js +0 -27
- package/clis/linux-do/category.js +0 -36
- package/clis/linux-do/hot.js +0 -25
- package/clis/linux-do/latest.js +0 -18
- package/clis/pixiv/test-utils.js +0 -23
- package/clis/toutiao/articles.test.js +0 -30
- package/dist/src/analysis.d.ts +0 -40
- package/dist/src/analysis.js +0 -172
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// coingecko derivatives — perpetual / futures tickers across crypto exchanges.
|
|
2
|
+
//
|
|
3
|
+
// Hits the public `/api/v3/derivatives` endpoint (no auth, free tier). Each
|
|
4
|
+
// row is one exchange-symbol combo: market, contract type, mark price, 24h
|
|
5
|
+
// %, basis vs index, funding rate, open interest (USD), 24h volume.
|
|
6
|
+
//
|
|
7
|
+
// CoinGecko sorts the response by 24h volume desc, so `rank` mirrors the
|
|
8
|
+
// listing order with no client-side reshuffling.
|
|
9
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
10
|
+
import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';
|
|
11
|
+
|
|
12
|
+
const ENDPOINT = 'https://api.coingecko.com/api/v3/derivatives';
|
|
13
|
+
|
|
14
|
+
cli({
|
|
15
|
+
site: 'coingecko',
|
|
16
|
+
name: 'derivatives',
|
|
17
|
+
access: 'read',
|
|
18
|
+
description: 'Top crypto derivative (perpetual / futures) markets by 24h volume',
|
|
19
|
+
domain: 'api.coingecko.com',
|
|
20
|
+
strategy: Strategy.PUBLIC,
|
|
21
|
+
browser: false,
|
|
22
|
+
args: [
|
|
23
|
+
{ name: 'limit', type: 'int', default: 20, help: 'Max rows to return (1-500; CoinGecko returns one large page).' },
|
|
24
|
+
{ name: 'symbol', type: 'string', required: false, help: 'Optional symbol substring filter (e.g. "BTC", "ETHUSDT").' },
|
|
25
|
+
],
|
|
26
|
+
columns: ['rank', 'market', 'symbol', 'indexId', 'contractType', 'price', 'change24hPct', 'fundingRate', 'openInterestUsd', 'volume24hUsd', 'expired'],
|
|
27
|
+
func: async (args) => {
|
|
28
|
+
const limit = Number(args.limit ?? 20);
|
|
29
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
30
|
+
throw new ArgumentError('coingecko derivatives limit must be a positive integer');
|
|
31
|
+
}
|
|
32
|
+
if (limit > 500) {
|
|
33
|
+
throw new ArgumentError('coingecko derivatives limit must be <= 500');
|
|
34
|
+
}
|
|
35
|
+
const filter = args.symbol == null ? '' : String(args.symbol).trim().toUpperCase();
|
|
36
|
+
let resp;
|
|
37
|
+
try {
|
|
38
|
+
resp = await fetch(ENDPOINT, { headers: { 'User-Agent': 'Mozilla/5.0' } });
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
throw new CommandExecutionError(`coingecko derivatives request failed: ${err?.message ?? err}`);
|
|
42
|
+
}
|
|
43
|
+
if (resp.status === 429) {
|
|
44
|
+
throw new CommandExecutionError(
|
|
45
|
+
'coingecko derivatives returned HTTP 429 (rate limited)',
|
|
46
|
+
'Free tier allows ~30 calls/min. Wait and retry.',
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (!resp.ok) {
|
|
50
|
+
throw new CommandExecutionError(`coingecko derivatives returned HTTP ${resp.status}`);
|
|
51
|
+
}
|
|
52
|
+
let data;
|
|
53
|
+
try {
|
|
54
|
+
data = await resp.json();
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
throw new CommandExecutionError(`coingecko derivatives returned malformed JSON: ${err?.message ?? err}`);
|
|
58
|
+
}
|
|
59
|
+
if (!Array.isArray(data) || !data.length) {
|
|
60
|
+
throw new EmptyResultError('coingecko derivatives', 'CoinGecko returned no derivative tickers.');
|
|
61
|
+
}
|
|
62
|
+
let rows = data;
|
|
63
|
+
if (filter) {
|
|
64
|
+
rows = data.filter((d) => String(d.symbol ?? '').toUpperCase().includes(filter)
|
|
65
|
+
|| String(d.index_id ?? '').toUpperCase().includes(filter));
|
|
66
|
+
if (!rows.length) {
|
|
67
|
+
throw new EmptyResultError('coingecko derivatives', `No derivative tickers matched symbol="${filter}".`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return rows.slice(0, limit).map((d, i) => ({
|
|
71
|
+
rank: i + 1,
|
|
72
|
+
market: String(d.market ?? ''),
|
|
73
|
+
symbol: String(d.symbol ?? ''),
|
|
74
|
+
indexId: String(d.index_id ?? ''),
|
|
75
|
+
contractType: String(d.contract_type ?? ''),
|
|
76
|
+
price: d.price != null ? Number(d.price) : null,
|
|
77
|
+
change24hPct: d.price_percentage_change_24h != null ? Number(d.price_percentage_change_24h) : null,
|
|
78
|
+
fundingRate: d.funding_rate != null ? Number(d.funding_rate) : null,
|
|
79
|
+
openInterestUsd: d.open_interest != null ? Number(d.open_interest) : null,
|
|
80
|
+
volume24hUsd: d.volume_24h != null ? Number(d.volume_24h) : null,
|
|
81
|
+
expired: d.expired_at ? String(d.expired_at) : '',
|
|
82
|
+
}));
|
|
83
|
+
},
|
|
84
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// coingecko exchanges — top crypto exchanges by 24h BTC trading volume.
|
|
2
|
+
//
|
|
3
|
+
// Hits the public `/api/v3/exchanges` endpoint. Returns the columns most
|
|
4
|
+
// useful for an agent: trust score, 24h BTC volume, country, year founded,
|
|
5
|
+
// canonical URL.
|
|
6
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
7
|
+
import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';
|
|
8
|
+
|
|
9
|
+
cli({
|
|
10
|
+
site: 'coingecko',
|
|
11
|
+
name: 'exchanges',
|
|
12
|
+
access: 'read',
|
|
13
|
+
description: 'Top crypto exchanges by 24h BTC trading volume',
|
|
14
|
+
domain: 'api.coingecko.com',
|
|
15
|
+
strategy: Strategy.PUBLIC,
|
|
16
|
+
browser: false,
|
|
17
|
+
args: [
|
|
18
|
+
{ name: 'limit', type: 'int', default: 20, help: 'Number of exchanges (1-250, CoinGecko per_page upper bound)' },
|
|
19
|
+
{ name: 'page', type: 'int', default: 1, help: 'Page number (1-based)' },
|
|
20
|
+
],
|
|
21
|
+
columns: ['rank', 'id', 'name', 'trustScore', 'volume24hBtc', 'country', 'yearEstablished', 'url'],
|
|
22
|
+
func: async (args) => {
|
|
23
|
+
const limit = Number(args.limit ?? 20);
|
|
24
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
25
|
+
throw new ArgumentError('coingecko limit must be a positive integer');
|
|
26
|
+
}
|
|
27
|
+
if (limit > 250) {
|
|
28
|
+
throw new ArgumentError('coingecko limit must be <= 250 (per_page upper bound)');
|
|
29
|
+
}
|
|
30
|
+
const page = Number(args.page ?? 1);
|
|
31
|
+
if (!Number.isInteger(page) || page <= 0) {
|
|
32
|
+
throw new ArgumentError('coingecko page must be a positive integer');
|
|
33
|
+
}
|
|
34
|
+
const url = new URL('https://api.coingecko.com/api/v3/exchanges');
|
|
35
|
+
url.searchParams.set('per_page', String(limit));
|
|
36
|
+
url.searchParams.set('page', String(page));
|
|
37
|
+
let resp;
|
|
38
|
+
try {
|
|
39
|
+
resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
throw new CommandExecutionError(`coingecko exchanges request failed: ${err?.message ?? err}`);
|
|
43
|
+
}
|
|
44
|
+
if (resp.status === 429) {
|
|
45
|
+
throw new CommandExecutionError(
|
|
46
|
+
'coingecko returned HTTP 429 (rate limited)',
|
|
47
|
+
'Free tier allows ~30 calls/min. Wait and retry.',
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (!resp.ok) {
|
|
51
|
+
throw new CommandExecutionError(`coingecko exchanges returned HTTP ${resp.status}`);
|
|
52
|
+
}
|
|
53
|
+
let data;
|
|
54
|
+
try {
|
|
55
|
+
data = await resp.json();
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new CommandExecutionError(`coingecko exchanges returned malformed JSON: ${err?.message ?? err}`);
|
|
59
|
+
}
|
|
60
|
+
if (!Array.isArray(data) || !data.length) {
|
|
61
|
+
throw new EmptyResultError('coingecko exchanges', 'CoinGecko returned no exchange data.');
|
|
62
|
+
}
|
|
63
|
+
return data.map((ex, i) => ({
|
|
64
|
+
rank: (page - 1) * limit + i + 1,
|
|
65
|
+
id: String(ex.id ?? ''),
|
|
66
|
+
name: String(ex.name ?? ''),
|
|
67
|
+
trustScore: ex.trust_score != null ? Number(ex.trust_score) : null,
|
|
68
|
+
volume24hBtc: ex.trade_volume_24h_btc != null ? Number(ex.trade_volume_24h_btc) : null,
|
|
69
|
+
country: String(ex.country ?? ''),
|
|
70
|
+
yearEstablished: ex.year_established != null ? Number(ex.year_established) : null,
|
|
71
|
+
url: String(ex.url ?? ''),
|
|
72
|
+
}));
|
|
73
|
+
},
|
|
74
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// coingecko global — total crypto market cap, volume, BTC/ETH dominance,
|
|
2
|
+
// active currencies, ICO counts, in a single row.
|
|
3
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
4
|
+
import { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';
|
|
5
|
+
|
|
6
|
+
cli({
|
|
7
|
+
site: 'coingecko',
|
|
8
|
+
name: 'global',
|
|
9
|
+
access: 'read',
|
|
10
|
+
description: 'Aggregate crypto market stats: total market cap, volume, dominance',
|
|
11
|
+
domain: 'api.coingecko.com',
|
|
12
|
+
strategy: Strategy.PUBLIC,
|
|
13
|
+
browser: false,
|
|
14
|
+
args: [
|
|
15
|
+
{ name: 'currency', type: 'string', default: 'usd', help: 'Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)' },
|
|
16
|
+
],
|
|
17
|
+
columns: ['currency', 'totalMarketCap', 'totalVolume24h', 'marketCapChange24hPct', 'btcDominancePct', 'ethDominancePct', 'activeCryptocurrencies', 'markets', 'ongoingIcos', 'updatedAt'],
|
|
18
|
+
func: async (args) => {
|
|
19
|
+
const currency = String(args.currency ?? 'usd').trim().toLowerCase();
|
|
20
|
+
if (!/^[a-z0-9-]{2,20}$/.test(currency)) {
|
|
21
|
+
throw new ArgumentError(`coingecko currency must look like a currency slug (got "${args.currency}")`);
|
|
22
|
+
}
|
|
23
|
+
let resp;
|
|
24
|
+
try {
|
|
25
|
+
resp = await fetch('https://api.coingecko.com/api/v3/global', { headers: { 'User-Agent': 'Mozilla/5.0' } });
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
throw new CommandExecutionError(`coingecko global request failed: ${err?.message ?? err}`);
|
|
29
|
+
}
|
|
30
|
+
if (resp.status === 429) {
|
|
31
|
+
throw new CommandExecutionError(
|
|
32
|
+
'coingecko returned HTTP 429 (rate limited)',
|
|
33
|
+
'Free tier allows ~30 calls/min. Wait and retry.',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if (!resp.ok) {
|
|
37
|
+
throw new CommandExecutionError(`coingecko global returned HTTP ${resp.status}`);
|
|
38
|
+
}
|
|
39
|
+
let body;
|
|
40
|
+
try {
|
|
41
|
+
body = await resp.json();
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
throw new CommandExecutionError(`coingecko global returned malformed JSON: ${err?.message ?? err}`);
|
|
45
|
+
}
|
|
46
|
+
const data = body?.data;
|
|
47
|
+
if (!data) {
|
|
48
|
+
throw new CommandExecutionError('coingecko global returned no data envelope');
|
|
49
|
+
}
|
|
50
|
+
const totalMarketCap = data?.total_market_cap?.[currency];
|
|
51
|
+
const totalVolume = data?.total_volume?.[currency];
|
|
52
|
+
if (totalMarketCap == null && totalVolume == null) {
|
|
53
|
+
throw new ArgumentError(
|
|
54
|
+
`coingecko has no market totals for currency "${currency}"`,
|
|
55
|
+
'Use a CoinGecko-supported quote currency such as usd, cny, eur, or jpy.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return [{
|
|
59
|
+
currency: currency.toUpperCase(),
|
|
60
|
+
totalMarketCap: totalMarketCap != null ? Number(totalMarketCap) : null,
|
|
61
|
+
totalVolume24h: totalVolume != null ? Number(totalVolume) : null,
|
|
62
|
+
marketCapChange24hPct: data.market_cap_change_percentage_24h_usd != null ? Number(data.market_cap_change_percentage_24h_usd) : null,
|
|
63
|
+
btcDominancePct: data?.market_cap_percentage?.btc != null ? Number(data.market_cap_percentage.btc) : null,
|
|
64
|
+
ethDominancePct: data?.market_cap_percentage?.eth != null ? Number(data.market_cap_percentage.eth) : null,
|
|
65
|
+
activeCryptocurrencies: data.active_cryptocurrencies != null ? Number(data.active_cryptocurrencies) : null,
|
|
66
|
+
markets: data.markets != null ? Number(data.markets) : null,
|
|
67
|
+
ongoingIcos: data.ongoing_icos != null ? Number(data.ongoing_icos) : null,
|
|
68
|
+
updatedAt: data.updated_at ? new Date(data.updated_at * 1000).toISOString() : '',
|
|
69
|
+
}];
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// coingecko top — top coins by market cap.
|
|
2
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
3
|
+
import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';
|
|
4
|
+
|
|
5
|
+
cli({
|
|
6
|
+
site: 'coingecko',
|
|
7
|
+
name: 'top',
|
|
8
|
+
access: 'read',
|
|
9
|
+
description: '按市值排序的加密货币行情(默认 USD)',
|
|
10
|
+
domain: 'api.coingecko.com',
|
|
11
|
+
strategy: Strategy.PUBLIC,
|
|
12
|
+
browser: false,
|
|
13
|
+
args: [
|
|
14
|
+
{ name: 'currency', type: 'string', default: 'usd', help: '计价币种 (usd / cny / eur / jpy ...)' },
|
|
15
|
+
{ name: 'limit', type: 'int', default: 10, help: '返回数量(默认 10,最多 250)' },
|
|
16
|
+
],
|
|
17
|
+
columns: ['rank', 'symbol', 'name', 'price', 'change24hPct', 'marketCap', 'volume24h', 'high24h', 'low24h'],
|
|
18
|
+
func: async (args) => {
|
|
19
|
+
const currency = String(args.currency ?? 'usd').toLowerCase();
|
|
20
|
+
const limit = Number(args.limit ?? 10);
|
|
21
|
+
if (!Number.isInteger(limit) || limit <= 0) {
|
|
22
|
+
throw new ArgumentError('limit must be a positive integer');
|
|
23
|
+
}
|
|
24
|
+
if (limit > 250) {
|
|
25
|
+
throw new ArgumentError('limit must be <= 250 (CoinGecko per_page upper bound)');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const url = new URL('https://api.coingecko.com/api/v3/coins/markets');
|
|
29
|
+
url.searchParams.set('vs_currency', currency);
|
|
30
|
+
url.searchParams.set('order', 'market_cap_desc');
|
|
31
|
+
url.searchParams.set('per_page', String(limit));
|
|
32
|
+
url.searchParams.set('page', '1');
|
|
33
|
+
url.searchParams.set('sparkline', 'false');
|
|
34
|
+
|
|
35
|
+
let resp;
|
|
36
|
+
try {
|
|
37
|
+
resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
|
|
38
|
+
} catch (error) {
|
|
39
|
+
throw new CommandExecutionError(`coingecko top request failed: ${error?.message || error}`);
|
|
40
|
+
}
|
|
41
|
+
if (!resp.ok) throw new CommandExecutionError(`coingecko top failed: HTTP ${resp.status}`);
|
|
42
|
+
let data;
|
|
43
|
+
try {
|
|
44
|
+
data = await resp.json();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw new CommandExecutionError(`coingecko returned malformed JSON: ${error?.message || error}`);
|
|
47
|
+
}
|
|
48
|
+
if (data?.error) throw new CommandExecutionError(`coingecko returned error: ${data.error}`);
|
|
49
|
+
if (!Array.isArray(data)) throw new CommandExecutionError('coingecko returned an unexpected response');
|
|
50
|
+
if (data.length === 0) throw new EmptyResultError('coingecko top', 'coingecko returned no market data');
|
|
51
|
+
|
|
52
|
+
return data.map((c) => ({
|
|
53
|
+
rank: c.market_cap_rank,
|
|
54
|
+
symbol: String(c.symbol ?? '').toUpperCase(),
|
|
55
|
+
name: c.name,
|
|
56
|
+
price: c.current_price,
|
|
57
|
+
change24hPct: c.price_change_percentage_24h,
|
|
58
|
+
marketCap: c.market_cap,
|
|
59
|
+
volume24h: c.total_volume,
|
|
60
|
+
high24h: c.high_24h,
|
|
61
|
+
low24h: c.low_24h,
|
|
62
|
+
}));
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// coingecko trending — top trending coins on CoinGecko (by user search activity).
|
|
2
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
3
|
+
import {
|
|
4
|
+
CommandExecutionError,
|
|
5
|
+
EmptyResultError,
|
|
6
|
+
} from '@jackwener/opencli/errors';
|
|
7
|
+
|
|
8
|
+
cli({
|
|
9
|
+
site: 'coingecko',
|
|
10
|
+
name: 'trending',
|
|
11
|
+
access: 'read',
|
|
12
|
+
description: 'Top trending cryptocurrencies on CoinGecko in the last 24h (search-volume based).',
|
|
13
|
+
domain: 'api.coingecko.com',
|
|
14
|
+
strategy: Strategy.PUBLIC,
|
|
15
|
+
browser: false,
|
|
16
|
+
args: [],
|
|
17
|
+
columns: ['rank', 'id', 'symbol', 'name', 'marketCapRank', 'priceBtc', 'thumb'],
|
|
18
|
+
func: async () => {
|
|
19
|
+
const url = 'https://api.coingecko.com/api/v3/search/trending';
|
|
20
|
+
let resp;
|
|
21
|
+
try {
|
|
22
|
+
resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw new CommandExecutionError(`coingecko trending request failed: ${error?.message || error}`);
|
|
25
|
+
}
|
|
26
|
+
if (resp.status === 429) {
|
|
27
|
+
throw new CommandExecutionError('coingecko returned HTTP 429 (rate limited)', 'Wait and retry.');
|
|
28
|
+
}
|
|
29
|
+
if (!resp.ok) {
|
|
30
|
+
throw new CommandExecutionError(`coingecko trending failed: HTTP ${resp.status}`);
|
|
31
|
+
}
|
|
32
|
+
let data;
|
|
33
|
+
try {
|
|
34
|
+
data = await resp.json();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new CommandExecutionError(`coingecko returned malformed JSON: ${error?.message || error}`);
|
|
37
|
+
}
|
|
38
|
+
const coins = Array.isArray(data?.coins) ? data.coins : [];
|
|
39
|
+
if (coins.length === 0) {
|
|
40
|
+
throw new EmptyResultError('coingecko trending', 'coingecko returned no trending coins.');
|
|
41
|
+
}
|
|
42
|
+
return coins.map((entry, i) => {
|
|
43
|
+
const c = entry?.item || {};
|
|
44
|
+
return {
|
|
45
|
+
rank: i + 1,
|
|
46
|
+
id: c.id || '',
|
|
47
|
+
symbol: String(c.symbol || '').toUpperCase(),
|
|
48
|
+
name: c.name || '',
|
|
49
|
+
marketCapRank: c.market_cap_rank ?? null,
|
|
50
|
+
priceBtc: c.price_btc ?? null,
|
|
51
|
+
thumb: c.thumb || c.small || c.large || '',
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@jackwener/opencli/errors';
|
|
1
2
|
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
-
import { canonicalizeProductUrl, normalizeProductId } from './utils.js';
|
|
3
|
+
import { canonicalizeProductUrl, normalizeProductId, requireProductIdArg } from './utils.js';
|
|
3
4
|
function escapeJsString(value) {
|
|
4
5
|
return JSON.stringify(value);
|
|
5
6
|
}
|
|
@@ -94,6 +95,7 @@ function buildAddToCartEvaluate(expectedProductId) {
|
|
|
94
95
|
cli({
|
|
95
96
|
site: 'coupang',
|
|
96
97
|
name: 'add-to-cart',
|
|
98
|
+
access: 'write',
|
|
97
99
|
description: 'Add a Coupang product to cart using logged-in browser session',
|
|
98
100
|
domain: 'www.coupang.com',
|
|
99
101
|
strategy: Strategy.COOKIE,
|
|
@@ -104,31 +106,38 @@ cli({
|
|
|
104
106
|
],
|
|
105
107
|
columns: ['ok', 'product_id', 'url', 'message'],
|
|
106
108
|
func: async (page, kwargs) => {
|
|
107
|
-
const rawProductId = kwargs['product-id']
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (!productId && !targetUrl) {
|
|
111
|
-
throw new Error('Either --product-id or --url is required');
|
|
109
|
+
const rawProductId = kwargs['product-id'];
|
|
110
|
+
if (!rawProductId && !kwargs.url) {
|
|
111
|
+
throw new ArgumentError('Either --product-id or --url is required');
|
|
112
112
|
}
|
|
113
|
+
const productId = rawProductId
|
|
114
|
+
? requireProductIdArg(rawProductId, 'product-id')
|
|
115
|
+
: requireProductIdArg(kwargs.url, '--url');
|
|
116
|
+
const targetUrl = canonicalizeProductUrl(kwargs.url, productId);
|
|
113
117
|
const finalUrl = targetUrl || canonicalizeProductUrl('', productId);
|
|
114
|
-
await page.goto(finalUrl)
|
|
115
|
-
|
|
118
|
+
await page.goto(finalUrl).catch((error) => {
|
|
119
|
+
throw new CommandExecutionError(`coupang add-to-cart navigation failed: ${error?.message || error}`);
|
|
120
|
+
});
|
|
121
|
+
const result = await page.evaluate(buildAddToCartEvaluate(productId)).catch((error) => {
|
|
122
|
+
throw new CommandExecutionError(`coupang add-to-cart evaluation failed: ${error?.message || error}`);
|
|
123
|
+
});
|
|
116
124
|
const loginHints = result?.loginHints ?? {};
|
|
117
125
|
if (loginHints.hasLoginLink && !loginHints.hasMyCoupang) {
|
|
118
|
-
throw new
|
|
126
|
+
throw new AuthRequiredError('coupang.com', 'Please log into Coupang in Chrome and retry.');
|
|
119
127
|
}
|
|
120
128
|
const actualProductId = normalizeProductId(result?.currentProductId || productId);
|
|
121
129
|
if (result?.reason === 'PRODUCT_MISMATCH') {
|
|
122
|
-
|
|
130
|
+
const observed = actualProductId ? `got ${actualProductId}` : 'no product id observed';
|
|
131
|
+
throw new CommandExecutionError(`Product mismatch: expected ${productId}, ${observed}`);
|
|
123
132
|
}
|
|
124
133
|
if (result?.reason === 'OPTION_REQUIRED') {
|
|
125
|
-
throw new
|
|
134
|
+
throw new CommandExecutionError('This product requires option selection and is not supported in v1.');
|
|
126
135
|
}
|
|
127
136
|
if (result?.reason === 'ADD_TO_CART_BUTTON_NOT_FOUND') {
|
|
128
|
-
throw new
|
|
137
|
+
throw new CommandExecutionError('Could not find an add-to-cart button on the product page.');
|
|
129
138
|
}
|
|
130
139
|
if (!result?.ok) {
|
|
131
|
-
throw new
|
|
140
|
+
throw new CommandExecutionError('Failed to confirm add-to-cart success.');
|
|
132
141
|
}
|
|
133
142
|
return [{
|
|
134
143
|
ok: true,
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';
|
|
3
|
+
import { getRegistry } from '@jackwener/opencli/registry';
|
|
4
|
+
import './search.js';
|
|
5
|
+
import './product.js';
|
|
6
|
+
import './add-to-cart.js';
|
|
7
|
+
import { parseLimitArg, parsePageArg, requireProductIdArg } from './utils.js';
|
|
8
|
+
|
|
9
|
+
describe('coupang utils — parseLimitArg / parsePageArg (no silent clamp)', () => {
|
|
10
|
+
it('parseLimitArg returns fallback for empty / undefined', () => {
|
|
11
|
+
expect(parseLimitArg(undefined, 20, 50)).toBe(20);
|
|
12
|
+
expect(parseLimitArg(null, 20, 50)).toBe(20);
|
|
13
|
+
expect(parseLimitArg('', 20, 50)).toBe(20);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('parseLimitArg accepts integers in range', () => {
|
|
17
|
+
expect(parseLimitArg(1, 20, 50)).toBe(1);
|
|
18
|
+
expect(parseLimitArg(50, 20, 50)).toBe(50);
|
|
19
|
+
expect(parseLimitArg('25', 20, 50)).toBe(25);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('parseLimitArg throws ArgumentError on out-of-range / non-integer (no silent clamp)', () => {
|
|
23
|
+
expect(() => parseLimitArg(0, 20, 50)).toThrow(ArgumentError);
|
|
24
|
+
expect(() => parseLimitArg(-1, 20, 50)).toThrow(ArgumentError);
|
|
25
|
+
expect(() => parseLimitArg(51, 20, 50)).toThrow(ArgumentError);
|
|
26
|
+
expect(() => parseLimitArg(999, 20, 50)).toThrow(ArgumentError);
|
|
27
|
+
expect(() => parseLimitArg('abc', 20, 50)).toThrow(ArgumentError);
|
|
28
|
+
expect(() => parseLimitArg(1.5, 20, 50)).toThrow(ArgumentError);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('parsePageArg returns fallback for empty', () => {
|
|
32
|
+
expect(parsePageArg(undefined, 1)).toBe(1);
|
|
33
|
+
expect(parsePageArg('', 1)).toBe(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('parsePageArg accepts positive integers', () => {
|
|
37
|
+
expect(parsePageArg(1, 1)).toBe(1);
|
|
38
|
+
expect(parsePageArg('5', 1)).toBe(5);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('parsePageArg throws ArgumentError on non-positive (no silent lift to 1)', () => {
|
|
42
|
+
expect(() => parsePageArg(0, 1)).toThrow(ArgumentError);
|
|
43
|
+
expect(() => parsePageArg(-1, 1)).toThrow(ArgumentError);
|
|
44
|
+
expect(() => parsePageArg('abc', 1)).toThrow(ArgumentError);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('coupang utils — product id validation', () => {
|
|
49
|
+
it('extracts numeric ids from ids and URLs', () => {
|
|
50
|
+
expect(requireProductIdArg('123456789')).toBe('123456789');
|
|
51
|
+
expect(requireProductIdArg('https://www.coupang.com/vp/products/123456789?itemId=1', '--url')).toBe('123456789');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('rejects malformed product ids instead of building fake URLs', () => {
|
|
55
|
+
expect(() => requireProductIdArg('abc')).toThrow(ArgumentError);
|
|
56
|
+
expect(() => requireProductIdArg('abc 123456789')).toThrow(ArgumentError);
|
|
57
|
+
expect(() => requireProductIdArg('https://www.coupang.com/not-a-product', '--url')).toThrow(ArgumentError);
|
|
58
|
+
expect(() => requireProductIdArg('https://www.coupang.com/not-a-product/123456789', '--url')).toThrow(ArgumentError);
|
|
59
|
+
expect(() => requireProductIdArg('https://notcoupang.com/vp/products/123456789', '--url')).toThrow(ArgumentError);
|
|
60
|
+
expect(() => requireProductIdArg('https://example.com/vp/products/123456789', '--url')).toThrow(ArgumentError);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('coupang adapter registry shape', () => {
|
|
65
|
+
it('search has product_id column for round-trip into product', () => {
|
|
66
|
+
const search = getRegistry().get('coupang/search');
|
|
67
|
+
expect(search).toBeDefined();
|
|
68
|
+
expect(search.access).toBe('read');
|
|
69
|
+
expect(search.columns).toContain('product_id');
|
|
70
|
+
// Listing pairs with detail: id-shaped column present.
|
|
71
|
+
const idShaped = search.columns.find((c) => /_id$|^id$/.test(c));
|
|
72
|
+
expect(idShaped).toBe('product_id');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('product cmd is a registered read adapter that pairs with search', () => {
|
|
76
|
+
const product = getRegistry().get('coupang/product');
|
|
77
|
+
expect(product).toBeDefined();
|
|
78
|
+
expect(product.access).toBe('read');
|
|
79
|
+
expect(product.columns).toContain('product_id');
|
|
80
|
+
expect(product.columns).toContain('title');
|
|
81
|
+
expect(product.columns).toContain('price');
|
|
82
|
+
expect(product.columns).toContain('seller');
|
|
83
|
+
expect(product.columns).toContain('rating');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('add-to-cart remains write-class', () => {
|
|
87
|
+
const cart = getRegistry().get('coupang/add-to-cart');
|
|
88
|
+
expect(cart).toBeDefined();
|
|
89
|
+
expect(cart.access).toBe('write');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('coupang search — typed errors (no silent fallback)', () => {
|
|
94
|
+
it('rejects empty query with ArgumentError', async () => {
|
|
95
|
+
const search = getRegistry().get('coupang/search');
|
|
96
|
+
// page object is irrelevant — we expect to fail before any browser call.
|
|
97
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
98
|
+
await expect(search.func(fakePage, { query: ' ' })).rejects.toThrow(ArgumentError);
|
|
99
|
+
await expect(search.func(fakePage, { query: '' })).rejects.toThrow(ArgumentError);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('rejects unsupported --filter with ArgumentError', async () => {
|
|
103
|
+
const search = getRegistry().get('coupang/search');
|
|
104
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
105
|
+
await expect(search.func(fakePage, { query: 'mouse', filter: 'eco' })).rejects.toThrow(ArgumentError);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('rejects out-of-range --limit with ArgumentError (no silent clamp to 50)', async () => {
|
|
109
|
+
const search = getRegistry().get('coupang/search');
|
|
110
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
111
|
+
await expect(search.func(fakePage, { query: 'mouse', limit: 999 })).rejects.toThrow(ArgumentError);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('rejects out-of-range --page with ArgumentError', async () => {
|
|
115
|
+
const search = getRegistry().get('coupang/search');
|
|
116
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
117
|
+
await expect(search.func(fakePage, { query: 'mouse', page: 0 })).rejects.toThrow(ArgumentError);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('coupang product — typed errors', () => {
|
|
122
|
+
it('rejects missing --product-id and --url with ArgumentError', async () => {
|
|
123
|
+
const product = getRegistry().get('coupang/product');
|
|
124
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
125
|
+
await expect(product.func(fakePage, {})).rejects.toThrow(ArgumentError);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('rejects malformed product id before navigation', async () => {
|
|
129
|
+
const product = getRegistry().get('coupang/product');
|
|
130
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
131
|
+
await expect(product.func(fakePage, { 'product-id': 'abc' })).rejects.toThrow(ArgumentError);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('wraps browser failures as CommandExecutionError', async () => {
|
|
135
|
+
const product = getRegistry().get('coupang/product');
|
|
136
|
+
const fakePage = { goto: () => Promise.reject(new Error('browser down')) };
|
|
137
|
+
await expect(product.func(fakePage, { 'product-id': '123456789' })).rejects.toThrow(CommandExecutionError);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('coupang add-to-cart — typed errors', () => {
|
|
142
|
+
it('rejects missing --product-id and --url with ArgumentError', async () => {
|
|
143
|
+
const cart = getRegistry().get('coupang/add-to-cart');
|
|
144
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
145
|
+
await expect(cart.func(fakePage, {})).rejects.toThrow(ArgumentError);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('rejects malformed product id before navigation', async () => {
|
|
149
|
+
const cart = getRegistry().get('coupang/add-to-cart');
|
|
150
|
+
const fakePage = { goto: () => { throw new Error('should not navigate'); } };
|
|
151
|
+
await expect(cart.func(fakePage, { 'product-id': 'abc' })).rejects.toThrow(ArgumentError);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('wraps browser failures as CommandExecutionError', async () => {
|
|
155
|
+
const cart = getRegistry().get('coupang/add-to-cart');
|
|
156
|
+
const fakePage = { goto: () => Promise.reject(new Error('browser down')) };
|
|
157
|
+
await expect(cart.func(fakePage, { 'product-id': '123456789' })).rejects.toThrow(CommandExecutionError);
|
|
158
|
+
});
|
|
159
|
+
});
|