@jackwener/opencli 1.7.10 → 1.7.12
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 +3 -3
- package/README.zh-CN.md +5 -4
- package/cli-manifest.json +1443 -24
- 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/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/amazon/bestsellers.js +1 -0
- package/clis/amazon/discussion.js +1 -0
- 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 +1 -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/arxiv/arxiv.test.js +112 -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 -0
- 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/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 +1 -0
- package/clis/chaoxing/exams.js +1 -0
- package/clis/chatgpt/image.js +1 -0
- package/clis/chatgpt-app/ask.js +1 -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 +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 +1 -0
- package/clis/claude/ask.js +1 -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/cnki/search.js +1 -0
- package/clis/codex/ask.js +1 -0
- package/clis/codex/export.js +1 -0
- package/clis/codex/extract-diff.js +1 -0
- package/clis/codex/history.js +1 -0
- package/clis/codex/model.js +1 -0
- package/clis/codex/read.js +1 -0
- package/clis/codex/send.js +1 -0
- package/clis/coupang/add-to-cart.js +1 -0
- package/clis/coupang/search.js +1 -0
- package/clis/ctrip/search.js +1 -0
- package/clis/cursor/ask.js +1 -0
- 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/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/deepseek/ask.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/status.js +1 -0
- package/clis/devto/devto.test.js +236 -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/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 +1 -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-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/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/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 +1 -0
- package/clis/facebook/profile.js +1 -0
- package/clis/facebook/search.js +1 -0
- package/clis/gemini/ask.js +1 -0
- package/clis/gemini/deep-research-result.js +1 -0
- package/clis/gemini/deep-research.js +1 -0
- package/clis/gemini/image.js +1 -0
- package/clis/gemini/new.js +1 -0
- 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 -0
- package/clis/google-scholar/profile.js +1 -0
- package/clis/google-scholar/search.js +1 -0
- package/clis/gov-law/recent.js +1 -0
- package/clis/gov-law/search.js +1 -0
- package/clis/gov-policy/recent.js +1 -0
- package/clis/gov-policy/search.js +1 -0
- package/clis/grok/ask.js +1 -0
- package/clis/grok/image.ts +1 -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/top.js +1 -0
- package/clis/hupu/detail.js +1 -0
- package/clis/hupu/hot.js +3 -1
- 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 -1
- 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 +92 -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 +1 -0
- package/clis/instagram/post.js +1 -0
- package/clis/instagram/profile.js +1 -0
- package/clis/instagram/reel.js +1 -0
- 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 +1 -0
- 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/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/linkedin/search.js +1 -0
- package/clis/linkedin/timeline.js +1 -0
- package/clis/linux-do/categories.js +1 -0
- package/clis/linux-do/category.js +1 -0
- package/clis/linux-do/feed.js +1 -0
- package/clis/linux-do/hot.js +1 -0
- package/clis/linux-do/latest.js +1 -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/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/medium/feed.js +1 -0
- package/clis/medium/search.js +1 -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/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/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/paperreview/feedback.js +1 -0
- package/clis/paperreview/review.js +1 -0
- package/clis/paperreview/submit.js +1 -0
- package/clis/pixiv/detail.js +1 -0
- package/clis/pixiv/download.js +1 -0
- package/clis/pixiv/illusts.js +2 -1
- package/clis/pixiv/ranking.js +2 -1
- package/clis/pixiv/search.js +2 -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/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/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/reuters/search.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/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/search.js +10 -2
- package/clis/stackoverflow/stackoverflow.test.js +346 -0
- package/clis/stackoverflow/unanswered.js +9 -2
- package/clis/steam/top-sellers.js +1 -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/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/ths/hot-rank.js +1 -0
- package/clis/tieba/hot.js +2 -1
- 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 +1 -0
- package/clis/tiktok/explore.js +1 -0
- package/clis/tiktok/follow.js +1 -0
- package/clis/tiktok/following.js +1 -0
- package/clis/tiktok/friends.js +1 -0
- package/clis/tiktok/like.js +1 -0
- package/clis/tiktok/live.js +1 -0
- package/clis/tiktok/notifications.js +1 -0
- package/clis/tiktok/profile.js +1 -0
- package/clis/tiktok/save.js +1 -0
- package/clis/tiktok/search.js +1 -0
- package/clis/tiktok/unfollow.js +1 -0
- package/clis/tiktok/unlike.js +1 -0
- package/clis/tiktok/unsave.js +1 -0
- package/clis/tiktok/user.js +1 -0
- package/clis/toutiao/articles.js +1 -0
- package/clis/twitter/accept.js +1 -0
- 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 +1 -0
- 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 +1 -0
- package/clis/twitter/reply.js +1 -0
- 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/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 -0
- package/clis/web/read.js +1 -0
- 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 +1 -0
- package/clis/weixin/download.js +1 -0
- package/clis/weixin/drafts.js +1 -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/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/xianyu/chat.js +1 -0
- package/clis/xianyu/item.js +1 -0
- package/clis/xianyu/search.js +1 -0
- package/clis/xiaoe/catalog.js +2 -1
- package/clis/xiaoe/content.js +1 -0
- package/clis/xiaoe/courses.js +1 -0
- package/clis/xiaoe/detail.js +1 -0
- package/clis/xiaoe/play-url.js +1 -0
- package/clis/xiaohongshu/comments.js +1 -0
- package/clis/xiaohongshu/creator-note-detail.js +1 -0
- package/clis/xiaohongshu/creator-notes-summary.js +1 -0
- 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 +1 -0
- 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 +1 -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 +1 -0
- package/clis/yuanbao/new.js +1 -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/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-shadow.d.ts +11 -0
- package/dist/src/adapter-shadow.js +72 -0
- package/dist/src/adapter-shadow.test.d.ts +1 -0
- package/dist/src/adapter-shadow.test.js +49 -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 +15 -2
- package/dist/src/browser/base-page.js +159 -8
- package/dist/src/browser/base-page.test.js +103 -1
- package/dist/src/browser/cdp.js +54 -0
- package/dist/src/browser/cdp.test.js +26 -0
- package/dist/src/browser/dom-helpers.d.ts +1 -1
- package/dist/src/browser/dom-helpers.js +15 -3
- package/dist/src/browser/page.d.ts +1 -0
- package/dist/src/browser/page.js +7 -1
- package/dist/src/browser/page.test.js +17 -0
- package/dist/src/browser/target-resolver.d.ts +17 -2
- package/dist/src/browser/target-resolver.js +85 -4
- package/dist/src/browser/verify-fixture.d.ts +7 -1
- package/dist/src/browser/verify-fixture.js +105 -0
- package/dist/src/browser/verify-fixture.test.js +59 -1
- package/dist/src/build-manifest.d.ts +68 -33
- package/dist/src/build-manifest.js +178 -29
- package/dist/src/build-manifest.test.js +85 -5
- package/dist/src/capabilityRouting.test.js +1 -1
- package/dist/src/cli.js +150 -11
- package/dist/src/cli.test.js +237 -0
- package/dist/src/commanderAdapter.d.ts +1 -1
- package/dist/src/commanderAdapter.js +17 -7
- package/dist/src/commanderAdapter.test.js +7 -6
- 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 +2 -0
- package/dist/src/doctor.d.ts +2 -0
- package/dist/src/doctor.js +6 -0
- package/dist/src/doctor.test.js +36 -1
- package/dist/src/engine.test.js +10 -10
- package/dist/src/execution.js +1 -1
- package/dist/src/execution.test.js +9 -9
- package/dist/src/help.d.ts +15 -0
- package/dist/src/help.js +149 -0
- package/dist/src/main.js +13 -7
- package/dist/src/manifest-types.d.ts +41 -0
- package/dist/src/manifest-types.js +9 -0
- package/dist/src/plugin.test.js +26 -26
- package/dist/src/registry.d.ts +5 -0
- package/dist/src/registry.js +8 -0
- package/dist/src/registry.test.js +27 -20
- package/dist/src/serialization.d.ts +4 -0
- package/dist/src/serialization.js +27 -1
- package/dist/src/serialization.test.js +24 -2
- package/dist/src/types.d.ts +2 -0
- package/package.json +5 -2
- 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/silent-column-drop-baseline.json +962 -0
- package/scripts/typed-error-lint-baseline.json +1586 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indeed keyword search.
|
|
3
|
+
*
|
|
4
|
+
* Drives the public `/jobs?q=&l=` listing page through a real browser
|
|
5
|
+
* because Cloudflare returns 403 to bare fetches. Extraction happens
|
|
6
|
+
* inside `page.evaluate` against the rendered DOM (Indeed's JSON-island
|
|
7
|
+
* is heavily obfuscated and changes shape per A/B bucket).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
11
|
+
import { EmptyResultError, CommandExecutionError } from '@jackwener/opencli/errors';
|
|
12
|
+
import {
|
|
13
|
+
SEARCH_COLUMNS,
|
|
14
|
+
requireQuery,
|
|
15
|
+
requireBoundedInt,
|
|
16
|
+
requireNonNegativeInt,
|
|
17
|
+
requireFromage,
|
|
18
|
+
requireSort,
|
|
19
|
+
buildSearchUrl,
|
|
20
|
+
searchCardToRow,
|
|
21
|
+
} from './utils.js';
|
|
22
|
+
|
|
23
|
+
cli({
|
|
24
|
+
site: 'indeed',
|
|
25
|
+
name: 'search',
|
|
26
|
+
access: 'read',
|
|
27
|
+
description: 'Indeed keyword job search (rendered DOM via browser session, US site)',
|
|
28
|
+
domain: 'www.indeed.com',
|
|
29
|
+
strategy: Strategy.COOKIE,
|
|
30
|
+
browser: true,
|
|
31
|
+
navigateBefore: false,
|
|
32
|
+
args: [
|
|
33
|
+
{ name: 'query', positional: true, required: true, help: 'Job keyword (title / skill / company)' },
|
|
34
|
+
{ name: 'location', type: 'string', default: '', help: 'Location filter (e.g. "remote", "New York, NY", "San Francisco")' },
|
|
35
|
+
{ name: 'fromage', type: 'string', default: '', help: 'Recency filter, days back: 1 / 3 / 7 / 14' },
|
|
36
|
+
{ name: 'sort', type: 'string', default: 'relevance', help: 'Sort order: relevance | date' },
|
|
37
|
+
{ name: 'start', type: 'int', default: 0, help: 'Pagination offset (multiple of 10, 0-based)' },
|
|
38
|
+
{ name: 'limit', type: 'int', default: 15, help: 'Max rows to return (1-25, capped at one page)' },
|
|
39
|
+
],
|
|
40
|
+
columns: SEARCH_COLUMNS,
|
|
41
|
+
func: async (page, kwargs) => {
|
|
42
|
+
const query = requireQuery(kwargs.query);
|
|
43
|
+
const location = String(kwargs.location ?? '').trim();
|
|
44
|
+
const fromage = requireFromage(kwargs.fromage);
|
|
45
|
+
const sort = requireSort(kwargs.sort);
|
|
46
|
+
const start = requireNonNegativeInt(kwargs.start, 0, 'start');
|
|
47
|
+
const limit = requireBoundedInt(kwargs.limit, 15, 25, 'limit');
|
|
48
|
+
|
|
49
|
+
const url = buildSearchUrl({ query, location, fromage, sort, start });
|
|
50
|
+
await page.goto(url);
|
|
51
|
+
await page.wait(4);
|
|
52
|
+
|
|
53
|
+
let cards;
|
|
54
|
+
try {
|
|
55
|
+
cards = await page.evaluate(`(async () => {
|
|
56
|
+
const hasResults = () => !!document.querySelector('.job_seen_beacon');
|
|
57
|
+
const hasEmptyState = () => {
|
|
58
|
+
const text = document.body?.innerText || '';
|
|
59
|
+
return !!document.querySelector('[data-testid="searchCountPages"], [data-testid="searchCount"], [data-testid="noResultsMessage"], [data-testid="empty-serp-result"]')
|
|
60
|
+
|| /did not match any jobs|no jobs found|0 jobs/i.test(text);
|
|
61
|
+
};
|
|
62
|
+
let ready = hasResults() || hasEmptyState();
|
|
63
|
+
for (let i = 0; i < 30; i++) {
|
|
64
|
+
if (ready) break;
|
|
65
|
+
await new Promise(r => setTimeout(r, 500));
|
|
66
|
+
ready = hasResults() || hasEmptyState();
|
|
67
|
+
}
|
|
68
|
+
const blocks = document.querySelectorAll('.job_seen_beacon');
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const b of blocks) {
|
|
72
|
+
const titleA = b.querySelector('h2.jobTitle a, [class*="jcs-JobTitle"]');
|
|
73
|
+
const jk = titleA?.getAttribute('data-jk');
|
|
74
|
+
if (!jk || seen.has(jk)) continue;
|
|
75
|
+
seen.add(jk);
|
|
76
|
+
const tags = Array.from(b.querySelectorAll('.metadataContainer li span'))
|
|
77
|
+
.map(s => (s.textContent || '').trim())
|
|
78
|
+
.filter(Boolean);
|
|
79
|
+
out.push({
|
|
80
|
+
jk,
|
|
81
|
+
title: b.querySelector('h2.jobTitle span')?.textContent?.trim() ?? '',
|
|
82
|
+
company: b.querySelector('[data-testid="company-name"]')?.textContent?.trim() ?? '',
|
|
83
|
+
location: b.querySelector('[data-testid="text-location"]')?.textContent?.trim() ?? '',
|
|
84
|
+
salary: b.querySelector('.salary-snippet-container span')?.textContent?.trim() ?? '',
|
|
85
|
+
tags,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const blockedHeadline = document.title || '';
|
|
89
|
+
const challenge = blockedHeadline.includes('Just a moment') || !!document.querySelector('[id^="cf-"]');
|
|
90
|
+
return { cards: out, challenge, ready };
|
|
91
|
+
})()`);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
throw new CommandExecutionError(`Failed to scrape Indeed search DOM: ${e?.message ?? e}`, 'The page may not have fully loaded; try again.');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (cards?.challenge) {
|
|
98
|
+
throw new CommandExecutionError('Indeed served a Cloudflare challenge page', 'Open https://www.indeed.com in the connected browser and clear the challenge, then retry.');
|
|
99
|
+
}
|
|
100
|
+
if (!cards?.ready) {
|
|
101
|
+
throw new CommandExecutionError('Indeed search page did not expose result or empty-state markers within 15s', 'Indeed may still be loading or the DOM shape may have changed; retry after opening Indeed in the connected browser.');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const list = Array.isArray(cards?.cards) ? cards.cards : [];
|
|
105
|
+
if (list.length === 0) {
|
|
106
|
+
throw new EmptyResultError('indeed search', `No Indeed jobs matched "${query}"${location ? ` in ${location}` : ''}`);
|
|
107
|
+
}
|
|
108
|
+
return list.slice(0, limit).map((c, i) => searchCardToRow(c, start + i + 1));
|
|
109
|
+
},
|
|
110
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indeed adapter utilities.
|
|
3
|
+
*
|
|
4
|
+
* Indeed sits behind Cloudflare and answers bare fetches with HTTP 403
|
|
5
|
+
* (`cf-mitigated: challenge`). The whole adapter therefore runs through a
|
|
6
|
+
* real browser session (Strategy.COOKIE), and DOM extraction lives inside
|
|
7
|
+
* `page.evaluate` blocks. The helpers below are mostly arg-validation +
|
|
8
|
+
* pure normalizers so they stay unit-testable without a browser.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { ArgumentError } from '@jackwener/opencli/errors';
|
|
12
|
+
|
|
13
|
+
export const INDEED_ORIGIN = 'https://www.indeed.com';
|
|
14
|
+
|
|
15
|
+
/** Job key (jk) shape — 16-char lowercase hex. */
|
|
16
|
+
const JK_PATTERN = /^[a-f0-9]{16}$/;
|
|
17
|
+
|
|
18
|
+
const FROMAGE_VALUES = new Set(['1', '3', '7', '14']);
|
|
19
|
+
|
|
20
|
+
const SORT_VALUES = new Set(['relevance', 'date']);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Coerce a value to a strict integer. Accepts numeric strings, rejects
|
|
24
|
+
* floats / non-numeric / NaN. Returns NaN on invalid input so callers can
|
|
25
|
+
* decide on the right typed error.
|
|
26
|
+
*/
|
|
27
|
+
export function coerceInt(value) {
|
|
28
|
+
if (value === undefined || value === null || value === '') return NaN;
|
|
29
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
30
|
+
return Number.isFinite(n) && Number.isInteger(n) ? n : NaN;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function requireBoundedInt(value, defaultValue, maxValue, label) {
|
|
34
|
+
const raw = value ?? defaultValue;
|
|
35
|
+
const n = coerceInt(raw);
|
|
36
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
37
|
+
throw new ArgumentError(`indeed ${label} must be a positive integer`);
|
|
38
|
+
}
|
|
39
|
+
if (n > maxValue) {
|
|
40
|
+
throw new ArgumentError(`indeed ${label} must be <= ${maxValue}`);
|
|
41
|
+
}
|
|
42
|
+
return n;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function requireNonNegativeInt(value, defaultValue, label) {
|
|
46
|
+
const raw = value ?? defaultValue;
|
|
47
|
+
const n = coerceInt(raw);
|
|
48
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
49
|
+
throw new ArgumentError(`indeed ${label} must be a non-negative integer`);
|
|
50
|
+
}
|
|
51
|
+
return n;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function requireJobKey(value) {
|
|
55
|
+
const id = String(value ?? '').trim().toLowerCase();
|
|
56
|
+
if (!id) {
|
|
57
|
+
throw new ArgumentError('indeed job id is required');
|
|
58
|
+
}
|
|
59
|
+
if (!JK_PATTERN.test(id)) {
|
|
60
|
+
throw new ArgumentError(`indeed job id "${value}" is not a valid jk (expected 16-char lowercase hex)`);
|
|
61
|
+
}
|
|
62
|
+
return id;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function requireQuery(value, label = 'query') {
|
|
66
|
+
const q = String(value ?? '').trim();
|
|
67
|
+
if (!q) {
|
|
68
|
+
throw new ArgumentError(`indeed ${label} cannot be empty`);
|
|
69
|
+
}
|
|
70
|
+
return q;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** "1" / "3" / "7" / "14" — accepted by Indeed's `fromage` filter. */
|
|
74
|
+
export function requireFromage(value) {
|
|
75
|
+
if (value === undefined || value === null || value === '') return '';
|
|
76
|
+
const v = String(value).trim();
|
|
77
|
+
if (!FROMAGE_VALUES.has(v)) {
|
|
78
|
+
throw new ArgumentError(`indeed fromage must be one of 1/3/7/14 (days), got "${value}"`);
|
|
79
|
+
}
|
|
80
|
+
return v;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function requireSort(value, defaultValue = 'relevance') {
|
|
84
|
+
const v = String(value ?? defaultValue).trim().toLowerCase();
|
|
85
|
+
if (!SORT_VALUES.has(v)) {
|
|
86
|
+
throw new ArgumentError(`indeed sort must be "relevance" or "date", got "${value}"`);
|
|
87
|
+
}
|
|
88
|
+
return v;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build an Indeed search URL with only the user-supplied filters set.
|
|
93
|
+
* Indeed treats absent params as defaults; we never pass empty strings
|
|
94
|
+
* because the page will echo them back into the query and the URL
|
|
95
|
+
* stays cleaner for round-tripping.
|
|
96
|
+
*/
|
|
97
|
+
export function buildSearchUrl({ query, location, fromage, sort, start }) {
|
|
98
|
+
const params = new URLSearchParams();
|
|
99
|
+
params.set('q', query);
|
|
100
|
+
if (location) params.set('l', location);
|
|
101
|
+
if (fromage) params.set('fromage', fromage);
|
|
102
|
+
if (sort && sort !== 'relevance') params.set('sort', sort);
|
|
103
|
+
if (start && start > 0) params.set('start', String(start));
|
|
104
|
+
return `${INDEED_ORIGIN}/jobs?${params.toString()}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function buildJobUrl(jk) {
|
|
108
|
+
return `${INDEED_ORIGIN}/viewjob?jk=${jk}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Strip a salary-snippet duplicate out of the metadata pill list. */
|
|
112
|
+
export function dedupeTags(tags, salary) {
|
|
113
|
+
const out = [];
|
|
114
|
+
for (const t of tags) {
|
|
115
|
+
const trimmed = String(t || '').trim();
|
|
116
|
+
if (!trimmed) continue;
|
|
117
|
+
if (salary && trimmed === salary) continue;
|
|
118
|
+
if (out.includes(trimmed)) continue;
|
|
119
|
+
out.push(trimmed);
|
|
120
|
+
}
|
|
121
|
+
return out.join(' · ');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Normalize a parsed search-card object into the row shape declared by
|
|
126
|
+
* `SEARCH_COLUMNS`. Drops nulls into empty strings, defends against
|
|
127
|
+
* Indeed surface drift by treating missing fields as empty rather than
|
|
128
|
+
* silently mislabeling them.
|
|
129
|
+
*/
|
|
130
|
+
export function searchCardToRow(card, rank) {
|
|
131
|
+
const jk = String(card?.jk ?? '').trim();
|
|
132
|
+
const salary = String(card?.salary ?? '').trim();
|
|
133
|
+
const tags = Array.isArray(card?.tags) ? card.tags : [];
|
|
134
|
+
return {
|
|
135
|
+
rank,
|
|
136
|
+
id: jk,
|
|
137
|
+
title: String(card?.title ?? '').replace(/\s+/g, ' ').trim(),
|
|
138
|
+
company: String(card?.company ?? '').replace(/\s+/g, ' ').trim(),
|
|
139
|
+
location: String(card?.location ?? '').replace(/\s+/g, ' ').trim(),
|
|
140
|
+
salary,
|
|
141
|
+
tags: dedupeTags(tags, salary),
|
|
142
|
+
url: jk ? buildJobUrl(jk) : '',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const SEARCH_COLUMNS = [
|
|
147
|
+
'rank', 'id', 'title', 'company', 'location', 'salary', 'tags', 'url',
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
export const JOB_COLUMNS = [
|
|
151
|
+
'id', 'title', 'company', 'location', 'salary', 'job_type', 'description', 'url',
|
|
152
|
+
];
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { cli } from '@jackwener/opencli/registry';
|
|
2
|
+
cli({
|
|
3
|
+
site: 'instagram',
|
|
4
|
+
name: 'collection-delete',
|
|
5
|
+
access: 'write',
|
|
6
|
+
description: 'Delete an Instagram saved-posts collection (folder) by name or id',
|
|
7
|
+
domain: 'www.instagram.com',
|
|
8
|
+
args: [
|
|
9
|
+
{
|
|
10
|
+
name: 'target',
|
|
11
|
+
required: true,
|
|
12
|
+
positional: true,
|
|
13
|
+
help: 'Collection name (case-insensitive) or numeric collection_id',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
columns: ['status', 'collectionId', 'collectionName'],
|
|
17
|
+
pipeline: [
|
|
18
|
+
{ navigate: 'https://www.instagram.com' },
|
|
19
|
+
{ evaluate: `(async () => {
|
|
20
|
+
const target = \${{ args.target | json }};
|
|
21
|
+
if (!target || !String(target).trim()) {
|
|
22
|
+
throw new Error('Collection target (name or id) cannot be empty');
|
|
23
|
+
}
|
|
24
|
+
const raw = String(target).trim();
|
|
25
|
+
const csrf = document.cookie.match(/csrftoken=([^;]+)/)?.[1] || '';
|
|
26
|
+
if (!csrf) {
|
|
27
|
+
throw new Error('csrftoken cookie missing - make sure you are logged in to Instagram');
|
|
28
|
+
}
|
|
29
|
+
const headers = { 'X-IG-App-ID': '936619743392459' };
|
|
30
|
+
|
|
31
|
+
// Resolve name -> id via /collections/list/. Always go through this path so we can
|
|
32
|
+
// surface an explicit error on duplicate names or unknown names instead of relying
|
|
33
|
+
// on a 404.
|
|
34
|
+
const listRes = await fetch('https://www.instagram.com/api/v1/collections/list/?collection_types=%5B%22MEDIA%22%5D', {
|
|
35
|
+
credentials: 'include',
|
|
36
|
+
headers,
|
|
37
|
+
});
|
|
38
|
+
if (!listRes.ok) {
|
|
39
|
+
throw new Error('Failed to list collections: HTTP ' + listRes.status + ' - make sure you are logged in to Instagram');
|
|
40
|
+
}
|
|
41
|
+
const listData = await listRes.json();
|
|
42
|
+
const collections = listData?.items || [];
|
|
43
|
+
const isNumericId = /^\\d{6,}$/.test(raw);
|
|
44
|
+
let id = '';
|
|
45
|
+
let resolvedName = '';
|
|
46
|
+
if (isNumericId) {
|
|
47
|
+
const hit = collections.find((c) => String(c?.collection_id) === raw);
|
|
48
|
+
if (!hit) {
|
|
49
|
+
throw new Error('Collection id not found in your account: ' + raw);
|
|
50
|
+
}
|
|
51
|
+
id = String(hit.collection_id);
|
|
52
|
+
resolvedName = String(hit.collection_name || '');
|
|
53
|
+
} else {
|
|
54
|
+
const wanted = raw.toLowerCase();
|
|
55
|
+
const matches = collections.filter((c) => String(c?.collection_name || '').trim().toLowerCase() === wanted);
|
|
56
|
+
if (matches.length === 0) {
|
|
57
|
+
const names = collections.map((c) => c?.collection_name).filter(Boolean);
|
|
58
|
+
throw new Error('Collection not found: ' + raw + '. Available: ' + (names.length ? names.join(', ') : '(none)'));
|
|
59
|
+
}
|
|
60
|
+
if (matches.length > 1) {
|
|
61
|
+
const ids = matches.map((c) => c.collection_id).join(', ');
|
|
62
|
+
throw new Error('Multiple collections share the name "' + raw + '" (ids: ' + ids + '). Pass the numeric collection_id explicitly to disambiguate.');
|
|
63
|
+
}
|
|
64
|
+
id = String(matches[0].collection_id);
|
|
65
|
+
resolvedName = String(matches[0].collection_name || raw);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const fd = new FormData();
|
|
69
|
+
fd.append('module_name', 'collection_settings');
|
|
70
|
+
const res = await fetch('https://www.instagram.com/api/v1/collections/' + encodeURIComponent(id) + '/delete/', {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
credentials: 'include',
|
|
73
|
+
headers: { ...headers, 'X-CSRFToken': csrf },
|
|
74
|
+
body: fd,
|
|
75
|
+
});
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const body = await res.text().catch(() => '');
|
|
78
|
+
throw new Error('Failed to delete collection: HTTP ' + res.status + (body ? ' - ' + body.slice(0, 200) : ''));
|
|
79
|
+
}
|
|
80
|
+
const d = await res.json().catch(() => ({}));
|
|
81
|
+
if (d?.status && d.status !== 'ok') {
|
|
82
|
+
throw new Error('Instagram returned non-ok status: ' + JSON.stringify(d).slice(0, 300));
|
|
83
|
+
}
|
|
84
|
+
return [{
|
|
85
|
+
status: 'Deleted',
|
|
86
|
+
collectionId: id,
|
|
87
|
+
collectionName: resolvedName,
|
|
88
|
+
}];
|
|
89
|
+
})()
|
|
90
|
+
` },
|
|
91
|
+
],
|
|
92
|
+
});
|
|
@@ -196,6 +196,7 @@ async function downloadInstagramMedia(items, outputDir) {
|
|
|
196
196
|
cli({
|
|
197
197
|
site: 'instagram',
|
|
198
198
|
name: 'download',
|
|
199
|
+
access: 'read',
|
|
199
200
|
description: 'Download images and videos from Instagram posts and reels',
|
|
200
201
|
domain: 'www.instagram.com',
|
|
201
202
|
strategy: Strategy.COOKIE,
|
package/clis/instagram/follow.js
CHANGED
package/clis/instagram/like.js
CHANGED
package/clis/instagram/note.js
CHANGED
package/clis/instagram/post.js
CHANGED
|
@@ -1397,6 +1397,7 @@ async function resolveLatestPostUrl(page, existingPostPaths) {
|
|
|
1397
1397
|
cli({
|
|
1398
1398
|
site: 'instagram',
|
|
1399
1399
|
name: 'post',
|
|
1400
|
+
access: 'write',
|
|
1400
1401
|
description: 'Post an Instagram feed image or mixed-media carousel',
|
|
1401
1402
|
domain: 'www.instagram.com',
|
|
1402
1403
|
strategy: Strategy.UI,
|
package/clis/instagram/reel.js
CHANGED
package/clis/instagram/save.js
CHANGED
package/clis/instagram/saved.js
CHANGED
package/clis/instagram/search.js
CHANGED
package/clis/instagram/story.js
CHANGED
package/clis/instagram/unlike.js
CHANGED
package/clis/instagram/unsave.js
CHANGED
package/clis/instagram/user.js
CHANGED
package/clis/jd/add-cart.js
CHANGED
package/clis/jd/cart.js
CHANGED
package/clis/jd/detail.js
CHANGED
package/clis/jd/item.js
CHANGED
package/clis/jd/reviews.js
CHANGED
package/clis/jd/search.js
CHANGED
package/clis/jianyu/detail.js
CHANGED
package/clis/jianyu/search.js
CHANGED
package/clis/jike/comment.js
CHANGED
package/clis/jike/create.js
CHANGED
package/clis/jike/feed.js
CHANGED
|
@@ -9,6 +9,7 @@ import { getPostDataJs } from './utils.js';
|
|
|
9
9
|
cli({
|
|
10
10
|
site: 'jike',
|
|
11
11
|
name: 'feed',
|
|
12
|
+
access: 'read',
|
|
12
13
|
description: '即刻首页动态流',
|
|
13
14
|
domain: 'web.okjike.com',
|
|
14
15
|
strategy: Strategy.COOKIE,
|
|
@@ -16,7 +17,7 @@ cli({
|
|
|
16
17
|
args: [
|
|
17
18
|
{ name: 'limit', type: 'int', default: 20 },
|
|
18
19
|
],
|
|
19
|
-
columns: ['author', 'content', 'likes', 'comments', 'time', 'url'],
|
|
20
|
+
columns: ['id', 'author', 'content', 'likes', 'comments', 'time', 'url'],
|
|
20
21
|
func: async (page, kwargs) => {
|
|
21
22
|
const limit = kwargs.limit || 20;
|
|
22
23
|
// 1. 导航到即刻首页,等待 SPA 重定向到 /following
|
|
@@ -43,6 +44,7 @@ cli({
|
|
|
43
44
|
if (!author && !content) continue;
|
|
44
45
|
|
|
45
46
|
results.push({
|
|
47
|
+
id: data.id,
|
|
46
48
|
author,
|
|
47
49
|
content: content.replace(/\\n/g, ' ').slice(0, 120),
|
|
48
50
|
likes: data.likeCount || 0,
|