@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
|
@@ -277,12 +277,13 @@ export function resolveTargetJs(ref, opts = {}) {
|
|
|
277
277
|
* Generate JS for click that uses the unified resolver.
|
|
278
278
|
* Assumes resolveTargetJs has been called and __resolved is set.
|
|
279
279
|
*/
|
|
280
|
-
export function clickResolvedJs() {
|
|
280
|
+
export function clickResolvedJs(opts = {}) {
|
|
281
|
+
const shouldScroll = opts.skipScroll ? 'false' : 'true';
|
|
281
282
|
return `
|
|
282
283
|
(() => {
|
|
283
284
|
const el = window.__resolved;
|
|
284
285
|
if (!el) throw new Error('No resolved element');
|
|
285
|
-
el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
286
|
+
if (${shouldScroll}) el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
286
287
|
const rect = el.getBoundingClientRect();
|
|
287
288
|
const x = Math.round(rect.left + rect.width / 2);
|
|
288
289
|
const y = Math.round(rect.top + rect.height / 2);
|
|
@@ -331,16 +332,96 @@ export function typeResolvedJs(text) {
|
|
|
331
332
|
})()
|
|
332
333
|
`;
|
|
333
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Prepare the resolved element for native CDP Input.insertText.
|
|
337
|
+
*
|
|
338
|
+
* This preserves `browser type`'s existing "replace current text" semantics:
|
|
339
|
+
* focus the editable target, select its current contents, then let CDP insert
|
|
340
|
+
* real browser text input so rich editors can update their internal state.
|
|
341
|
+
*/
|
|
342
|
+
export function prepareNativeTypeResolvedJs(opts = {}) {
|
|
343
|
+
const shouldScroll = opts.skipScroll ? 'false' : 'true';
|
|
344
|
+
const shouldFocus = opts.skipFocus ? 'false' : 'true';
|
|
345
|
+
return `
|
|
346
|
+
(() => {
|
|
347
|
+
const original = window.__resolved;
|
|
348
|
+
if (!original) throw new Error('No resolved element');
|
|
349
|
+
|
|
350
|
+
function nearestContentEditableHost(el) {
|
|
351
|
+
let current = el;
|
|
352
|
+
while (current && current.nodeType === 1) {
|
|
353
|
+
if (current.hasAttribute && current.hasAttribute('contenteditable')) return current;
|
|
354
|
+
current = current.parentElement;
|
|
355
|
+
}
|
|
356
|
+
return el.isContentEditable ? el : null;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const editableHost = original.isContentEditable ? nearestContentEditableHost(original) : null;
|
|
360
|
+
const inputTypes = new Set(['', 'text', 'search', 'url', 'tel', 'email', 'password']);
|
|
361
|
+
const isInput = original instanceof HTMLInputElement;
|
|
362
|
+
const isTextarea = original instanceof HTMLTextAreaElement;
|
|
363
|
+
const isTextControl = isTextarea || (isInput && inputTypes.has((original.getAttribute('type') || original.type || '').toLowerCase()));
|
|
364
|
+
const el = editableHost || (isTextControl ? original : null);
|
|
365
|
+
|
|
366
|
+
if (!el) {
|
|
367
|
+
return {
|
|
368
|
+
ok: false,
|
|
369
|
+
reason: 'not_editable',
|
|
370
|
+
tag: original.tagName ? original.tagName.toLowerCase() : '',
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
window.__resolved = el;
|
|
375
|
+
if (${shouldScroll}) el.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
|
|
376
|
+
if (${shouldFocus}) {
|
|
377
|
+
try {
|
|
378
|
+
el.focus({ preventScroll: true });
|
|
379
|
+
} catch (_) {
|
|
380
|
+
el.focus();
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (editableHost) {
|
|
385
|
+
const sel = window.getSelection();
|
|
386
|
+
if (!sel) return { ok: false, reason: 'selection_unavailable', mode: 'contenteditable' };
|
|
387
|
+
const range = document.createRange();
|
|
388
|
+
range.selectNodeContents(el);
|
|
389
|
+
sel.removeAllRanges();
|
|
390
|
+
sel.addRange(range);
|
|
391
|
+
return { ok: true, mode: 'contenteditable' };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
let selected = false;
|
|
395
|
+
try {
|
|
396
|
+
if (typeof el.setSelectionRange === 'function') {
|
|
397
|
+
el.setSelectionRange(0, String(el.value || '').length);
|
|
398
|
+
selected = true;
|
|
399
|
+
}
|
|
400
|
+
} catch (_) {}
|
|
401
|
+
try {
|
|
402
|
+
if (!selected && typeof el.select === 'function') {
|
|
403
|
+
el.select();
|
|
404
|
+
selected = true;
|
|
405
|
+
}
|
|
406
|
+
} catch (_) {}
|
|
407
|
+
|
|
408
|
+
return selected
|
|
409
|
+
? { ok: true, mode: isTextarea ? 'textarea' : 'input' }
|
|
410
|
+
: { ok: false, reason: 'selection_unavailable', mode: isTextarea ? 'textarea' : 'input' };
|
|
411
|
+
})()
|
|
412
|
+
`;
|
|
413
|
+
}
|
|
334
414
|
/**
|
|
335
415
|
* Generate JS for scrollTo that uses the unified resolver.
|
|
336
416
|
* Assumes resolveTargetJs has been called and __resolved is set.
|
|
337
417
|
*/
|
|
338
|
-
export function scrollResolvedJs() {
|
|
418
|
+
export function scrollResolvedJs(opts = {}) {
|
|
419
|
+
const shouldScroll = opts.skipScroll ? 'false' : 'true';
|
|
339
420
|
return `
|
|
340
421
|
(() => {
|
|
341
422
|
const el = window.__resolved;
|
|
342
423
|
if (!el) throw new Error('No resolved element');
|
|
343
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
|
|
424
|
+
if (${shouldScroll}) el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
|
|
344
425
|
return { scrolled: true, tag: el.tagName.toLowerCase(), text: (el.textContent || '').trim().slice(0, 80) };
|
|
345
426
|
})()
|
|
346
427
|
`;
|
|
@@ -31,11 +31,15 @@ export type Fixture = {
|
|
|
31
31
|
expect?: FixtureExpect;
|
|
32
32
|
};
|
|
33
33
|
export type ValidationFailure = {
|
|
34
|
-
rule: 'rowCount' | 'column' | 'type' | 'pattern' | 'notEmpty' | 'mustNotContain' | 'mustBeTruthy';
|
|
34
|
+
rule: 'rowCount' | 'column' | 'type' | 'pattern' | 'notEmpty' | 'mustNotContain' | 'mustBeTruthy' | 'shapeKeyCount' | 'shapeDepth' | 'shapeNestedId';
|
|
35
35
|
detail: string;
|
|
36
36
|
rowIndex?: number;
|
|
37
37
|
};
|
|
38
38
|
export type Row = Record<string, unknown>;
|
|
39
|
+
export type RowShapeOptions = {
|
|
40
|
+
maxTopLevelKeys?: number;
|
|
41
|
+
maxNestedDepth?: number;
|
|
42
|
+
};
|
|
39
43
|
export declare function fixturePath(site: string, command: string): string;
|
|
40
44
|
export declare function loadFixture(site: string, command: string): Fixture | null;
|
|
41
45
|
export declare function writeFixture(site: string, command: string, fixture: Fixture): string;
|
|
@@ -51,9 +55,11 @@ export declare function writeFixture(site: string, command: string, fixture: Fix
|
|
|
51
55
|
*/
|
|
52
56
|
export declare function deriveFixture(rows: Row[], args?: FixtureArgs): Fixture;
|
|
53
57
|
export declare function validateRows(rows: Row[], fixture: Fixture): ValidationFailure[];
|
|
58
|
+
export declare function validateRowShape(rows: Row[], opts?: RowShapeOptions): ValidationFailure[];
|
|
54
59
|
/**
|
|
55
60
|
* Convert fixture args into argv tokens appended after the command name.
|
|
56
61
|
* - Array form is passed through verbatim (stringified), supporting positional subjects.
|
|
57
62
|
* - Object form is expanded to `--key value` pairs.
|
|
58
63
|
*/
|
|
59
64
|
export declare function expandFixtureArgs(args: FixtureArgs | undefined): string[];
|
|
65
|
+
export declare function parseSeedArgs(raw: string | undefined): FixtureArgs | undefined;
|
|
@@ -30,6 +30,26 @@
|
|
|
30
30
|
import * as fs from 'node:fs';
|
|
31
31
|
import * as os from 'node:os';
|
|
32
32
|
import * as path from 'node:path';
|
|
33
|
+
const DEFAULT_MAX_TOP_LEVEL_KEYS = 12;
|
|
34
|
+
const DEFAULT_MAX_NESTED_DEPTH = 1;
|
|
35
|
+
const ID_SHAPED_KEY_PATTERNS = [
|
|
36
|
+
/^id$/i,
|
|
37
|
+
/_id$/i,
|
|
38
|
+
/Id$/,
|
|
39
|
+
/^short_id$/i,
|
|
40
|
+
/^bvid$/i,
|
|
41
|
+
/^aid$/i,
|
|
42
|
+
/^tid$/i,
|
|
43
|
+
/^asin$/i,
|
|
44
|
+
/^sku$/i,
|
|
45
|
+
/^isbn$/i,
|
|
46
|
+
/^doi$/i,
|
|
47
|
+
/^slug$/i,
|
|
48
|
+
/^hn_id$/i,
|
|
49
|
+
/^username$/i,
|
|
50
|
+
/^handle$/i,
|
|
51
|
+
/^uri$/i,
|
|
52
|
+
];
|
|
33
53
|
export function fixturePath(site, command) {
|
|
34
54
|
return path.join(os.homedir(), '.opencli', 'sites', site, 'verify', `${command}.json`);
|
|
35
55
|
}
|
|
@@ -180,6 +200,39 @@ export function validateRows(rows, fixture) {
|
|
|
180
200
|
});
|
|
181
201
|
return failures;
|
|
182
202
|
}
|
|
203
|
+
export function validateRowShape(rows, opts = {}) {
|
|
204
|
+
const failures = [];
|
|
205
|
+
const maxTopLevelKeys = opts.maxTopLevelKeys ?? DEFAULT_MAX_TOP_LEVEL_KEYS;
|
|
206
|
+
const maxNestedDepth = opts.maxNestedDepth ?? DEFAULT_MAX_NESTED_DEPTH;
|
|
207
|
+
rows.forEach((row, i) => {
|
|
208
|
+
const keys = Object.keys(row);
|
|
209
|
+
if (keys.length > maxTopLevelKeys) {
|
|
210
|
+
failures.push({
|
|
211
|
+
rule: 'shapeKeyCount',
|
|
212
|
+
detail: `row has ${keys.length} top-level keys, expected at most ${maxTopLevelKeys}`,
|
|
213
|
+
rowIndex: i,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
for (const [key, value] of Object.entries(row)) {
|
|
217
|
+
const depth = nestedDepth(value);
|
|
218
|
+
if (depth > maxNestedDepth) {
|
|
219
|
+
failures.push({
|
|
220
|
+
rule: 'shapeDepth',
|
|
221
|
+
detail: `"${key}" nesting depth is ${depth}, expected at most ${maxNestedDepth}`,
|
|
222
|
+
rowIndex: i,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
for (const path of nestedIdPaths(value, key)) {
|
|
226
|
+
failures.push({
|
|
227
|
+
rule: 'shapeNestedId',
|
|
228
|
+
detail: `id-shaped field "${path}" must be a top-level row key`,
|
|
229
|
+
rowIndex: i,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return failures;
|
|
235
|
+
}
|
|
183
236
|
/**
|
|
184
237
|
* Convert fixture args into argv tokens appended after the command name.
|
|
185
238
|
* - Array form is passed through verbatim (stringified), supporting positional subjects.
|
|
@@ -196,6 +249,24 @@ export function expandFixtureArgs(args) {
|
|
|
196
249
|
}
|
|
197
250
|
return out;
|
|
198
251
|
}
|
|
252
|
+
export function parseSeedArgs(raw) {
|
|
253
|
+
if (raw === undefined)
|
|
254
|
+
return undefined;
|
|
255
|
+
const trimmed = raw.trim();
|
|
256
|
+
if (!trimmed)
|
|
257
|
+
return undefined;
|
|
258
|
+
try {
|
|
259
|
+
const parsed = JSON.parse(trimmed);
|
|
260
|
+
if (Array.isArray(parsed))
|
|
261
|
+
return parsed;
|
|
262
|
+
if (parsed !== null && typeof parsed === 'object')
|
|
263
|
+
return parsed;
|
|
264
|
+
return [parsed];
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return [raw];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
199
270
|
function jsType(v) {
|
|
200
271
|
if (v === null)
|
|
201
272
|
return 'null';
|
|
@@ -203,6 +274,40 @@ function jsType(v) {
|
|
|
203
274
|
return 'array';
|
|
204
275
|
return typeof v;
|
|
205
276
|
}
|
|
277
|
+
function nestedDepth(value) {
|
|
278
|
+
if (value === null || value === undefined || typeof value !== 'object')
|
|
279
|
+
return 0;
|
|
280
|
+
if (Array.isArray(value)) {
|
|
281
|
+
if (value.length === 0)
|
|
282
|
+
return 1;
|
|
283
|
+
return 1 + Math.max(...value.map(nestedDepth));
|
|
284
|
+
}
|
|
285
|
+
const values = Object.values(value);
|
|
286
|
+
if (values.length === 0)
|
|
287
|
+
return 1;
|
|
288
|
+
return 1 + Math.max(...values.map(nestedDepth));
|
|
289
|
+
}
|
|
290
|
+
function nestedIdPaths(value, prefix) {
|
|
291
|
+
if (value === null || value === undefined || typeof value !== 'object')
|
|
292
|
+
return [];
|
|
293
|
+
const paths = [];
|
|
294
|
+
if (Array.isArray(value)) {
|
|
295
|
+
value.forEach((item, index) => {
|
|
296
|
+
paths.push(...nestedIdPaths(item, `${prefix}[${index}]`));
|
|
297
|
+
});
|
|
298
|
+
return paths;
|
|
299
|
+
}
|
|
300
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
301
|
+
const childPath = `${prefix}.${key}`;
|
|
302
|
+
if (isIdShapedKey(key))
|
|
303
|
+
paths.push(childPath);
|
|
304
|
+
paths.push(...nestedIdPaths(nested, childPath));
|
|
305
|
+
}
|
|
306
|
+
return paths;
|
|
307
|
+
}
|
|
308
|
+
function isIdShapedKey(key) {
|
|
309
|
+
return ID_SHAPED_KEY_PATTERNS.some((pattern) => pattern.test(key));
|
|
310
|
+
}
|
|
206
311
|
function typeMatches(actual, declared) {
|
|
207
312
|
const allowed = declared.split('|').map((s) => s.trim()).filter(Boolean);
|
|
208
313
|
if (allowed.length === 0)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { deriveFixture, expandFixtureArgs, validateRows } from './verify-fixture.js';
|
|
2
|
+
import { deriveFixture, expandFixtureArgs, parseSeedArgs, validateRows, validateRowShape } from './verify-fixture.js';
|
|
3
3
|
describe('validateRows', () => {
|
|
4
4
|
it('passes when rows meet all expectations', () => {
|
|
5
5
|
const fixture = {
|
|
@@ -98,6 +98,49 @@ describe('validateRows', () => {
|
|
|
98
98
|
expect(failures.map((f) => f.rowIndex)).toEqual([1, 2, 3, 4]);
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
|
+
describe('validateRowShape', () => {
|
|
102
|
+
it('passes flat rows with a compact key set', () => {
|
|
103
|
+
const failures = validateRowShape([
|
|
104
|
+
{ id: '1', title: 'A', author: { name: 'Ada' }, tags: ['ai', 'web'] },
|
|
105
|
+
]);
|
|
106
|
+
expect(failures).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
it('reports rows with too many top-level keys', () => {
|
|
109
|
+
const row = Object.fromEntries(Array.from({ length: 13 }, (_, i) => [`k${i}`, i]));
|
|
110
|
+
const failures = validateRowShape([row]);
|
|
111
|
+
expect(failures).toEqual([
|
|
112
|
+
{
|
|
113
|
+
rule: 'shapeKeyCount',
|
|
114
|
+
detail: 'row has 13 top-level keys, expected at most 12',
|
|
115
|
+
rowIndex: 0,
|
|
116
|
+
},
|
|
117
|
+
]);
|
|
118
|
+
});
|
|
119
|
+
it('reports nesting deeper than one level', () => {
|
|
120
|
+
const failures = validateRowShape([
|
|
121
|
+
{ title: 'A', stats: { author: { name: 'Ada' } } },
|
|
122
|
+
]);
|
|
123
|
+
expect(failures).toEqual([
|
|
124
|
+
{
|
|
125
|
+
rule: 'shapeDepth',
|
|
126
|
+
detail: '"stats" nesting depth is 2, expected at most 1',
|
|
127
|
+
rowIndex: 0,
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
it('reports nested id-shaped fields even when one-level nesting is otherwise allowed', () => {
|
|
132
|
+
const failures = validateRowShape([
|
|
133
|
+
{ title: 'A', author: { user_id: 'u1', name: 'Ada' } },
|
|
134
|
+
]);
|
|
135
|
+
expect(failures).toEqual([
|
|
136
|
+
{
|
|
137
|
+
rule: 'shapeNestedId',
|
|
138
|
+
detail: 'id-shaped field "author.user_id" must be a top-level row key',
|
|
139
|
+
rowIndex: 0,
|
|
140
|
+
},
|
|
141
|
+
]);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
101
144
|
describe('deriveFixture', () => {
|
|
102
145
|
it('returns rowCount.min=0 when rows are empty', () => {
|
|
103
146
|
expect(deriveFixture([])).toEqual({ expect: { rowCount: { min: 0 } } });
|
|
@@ -159,3 +202,18 @@ describe('expandFixtureArgs', () => {
|
|
|
159
202
|
]);
|
|
160
203
|
});
|
|
161
204
|
});
|
|
205
|
+
describe('parseSeedArgs', () => {
|
|
206
|
+
it('treats plain text as one positional arg', () => {
|
|
207
|
+
expect(parseSeedArgs('opencli-verify')).toEqual(['opencli-verify']);
|
|
208
|
+
});
|
|
209
|
+
it('accepts JSON array seed args', () => {
|
|
210
|
+
expect(parseSeedArgs('["subject", "--limit", 3]')).toEqual(['subject', '--limit', 3]);
|
|
211
|
+
});
|
|
212
|
+
it('accepts JSON object seed args', () => {
|
|
213
|
+
expect(parseSeedArgs('{"limit":3,"sort":"hot"}')).toEqual({ limit: 3, sort: 'hot' });
|
|
214
|
+
});
|
|
215
|
+
it('ignores empty input', () => {
|
|
216
|
+
expect(parseSeedArgs(undefined)).toBeUndefined();
|
|
217
|
+
expect(parseSeedArgs(' ')).toBeUndefined();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -5,41 +5,76 @@
|
|
|
5
5
|
* Scans all JS CLI definitions in clis/ and pre-compiles them into a single
|
|
6
6
|
* manifest.json for instant cold-start registration.
|
|
7
7
|
*
|
|
8
|
-
* Usage: npx tsx src/build-manifest.ts
|
|
8
|
+
* Usage: npx tsx src/build-manifest.ts [--allow-removals[=N]]
|
|
9
|
+
*
|
|
9
10
|
* Output: cli-manifest.json next to clis/
|
|
11
|
+
*
|
|
12
|
+
* Safety invariants:
|
|
13
|
+
* - Adapters whose source file does not call `cli(...)` are silently
|
|
14
|
+
* skipped (they are helpers / type modules, not commands).
|
|
15
|
+
* - Adapters that look like commands but fail to import are reported as
|
|
16
|
+
* failures, the manifest is NOT written, and the process exits 1. This
|
|
17
|
+
* prevents a stale dist or a broken adapter from silently dropping
|
|
18
|
+
* other adapters' entries (root cause of the "manifest lost 478 lines"
|
|
19
|
+
* incident).
|
|
20
|
+
* - Net-deletions vs the existing committed manifest abort the build by
|
|
21
|
+
* default; pass `--allow-removals=N` (or just `--allow-removals` for any
|
|
22
|
+
* amount) to confirm an intentional removal.
|
|
23
|
+
*/
|
|
24
|
+
import type { ManifestEntry } from './manifest-types.js';
|
|
25
|
+
export type { ManifestEntry } from './manifest-types.js';
|
|
26
|
+
/**
|
|
27
|
+
* Thrown by `loadManifestEntries` when an adapter file looks like a CLI
|
|
28
|
+
* module (matches CLI_MODULE_PATTERN) but cannot be imported. Callers
|
|
29
|
+
* decide whether to abort or aggregate failures across the whole scan.
|
|
10
30
|
*/
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
positional?: boolean;
|
|
26
|
-
help?: string;
|
|
27
|
-
choices?: string[];
|
|
28
|
-
}>;
|
|
29
|
-
columns?: string[];
|
|
30
|
-
pipeline?: Record<string, unknown>[];
|
|
31
|
-
timeout?: number;
|
|
32
|
-
deprecated?: boolean | string;
|
|
33
|
-
replacedBy?: string;
|
|
34
|
-
type: 'js';
|
|
35
|
-
/** Relative path from clis/ dir, e.g. 'bilibili/search.js' */
|
|
36
|
-
modulePath?: string;
|
|
37
|
-
/** Relative path to the source file from clis/ dir (e.g. 'site/cmd.js') */
|
|
38
|
-
sourceFile?: string;
|
|
39
|
-
/** Pre-navigation control — see CliCommand.navigateBefore */
|
|
40
|
-
navigateBefore?: boolean | string;
|
|
31
|
+
export declare class ManifestImportError extends Error {
|
|
32
|
+
readonly filePath: string;
|
|
33
|
+
readonly cause: unknown;
|
|
34
|
+
constructor(filePath: string, cause: unknown);
|
|
35
|
+
}
|
|
36
|
+
export interface BuildManifestResult {
|
|
37
|
+
entries: ManifestEntry[];
|
|
38
|
+
/** Adapters that look like CLI modules but failed to import. */
|
|
39
|
+
failures: ManifestImportError[];
|
|
40
|
+
}
|
|
41
|
+
export interface BuildManifestArgs {
|
|
42
|
+
/** Maximum number of entries that may be removed vs the existing manifest.
|
|
43
|
+
* `Number.POSITIVE_INFINITY` disables the safety net entirely. */
|
|
44
|
+
allowRemovals: number;
|
|
41
45
|
}
|
|
42
46
|
export declare function normalizeManifestPath(relativePath: string): string;
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Load all manifest entries from a single adapter file.
|
|
49
|
+
*
|
|
50
|
+
* Returns `[]` for files that do not register a CLI command (helpers, types).
|
|
51
|
+
* Throws `ManifestImportError` when a file looks like a CLI module but its
|
|
52
|
+
* import or post-import processing fails — callers must decide whether to
|
|
53
|
+
* surface or aggregate the failure.
|
|
54
|
+
*
|
|
55
|
+
* The third argument `clisDir` is used to compute the POSIX-style
|
|
56
|
+
* `sourceFile` relative path; it defaults to the package's `clis/` dir so
|
|
57
|
+
* existing test callers stay backward-compatible.
|
|
58
|
+
*/
|
|
59
|
+
export declare function loadManifestEntries(filePath: string, site: string, importer?: (moduleHref: string) => Promise<unknown>, clisDir?: string): Promise<ManifestEntry[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Scan a `clis/` directory and aggregate per-adapter results. Import
|
|
62
|
+
* failures are collected in `failures` instead of crashing the whole scan,
|
|
63
|
+
* but the caller (e.g. `main()`) is expected to fail loud if any failure
|
|
64
|
+
* is present.
|
|
65
|
+
*/
|
|
66
|
+
export declare function scanClisDir(clisDir: string, importer?: (moduleHref: string) => Promise<unknown>): Promise<BuildManifestResult>;
|
|
67
|
+
export declare function buildManifest(): Promise<BuildManifestResult>;
|
|
45
68
|
export declare function serializeManifest(manifest: ManifestEntry[]): string;
|
|
69
|
+
/**
|
|
70
|
+
* Diff helper: returns site/name keys that exist in `prev` but not in
|
|
71
|
+
* `next`. Used as a safety net to detect accidental mass-deletions caused
|
|
72
|
+
* by silently failing adapter imports.
|
|
73
|
+
*/
|
|
74
|
+
export declare function diffRemovedEntries(prev: readonly ManifestEntry[], next: readonly ManifestEntry[]): string[];
|
|
75
|
+
/**
|
|
76
|
+
* Parse `--allow-removals` and `--allow-removals=N` from argv.
|
|
77
|
+
* Bare `--allow-removals` disables the safety net (`Infinity`); the
|
|
78
|
+
* numeric form sets an explicit upper bound.
|
|
79
|
+
*/
|
|
80
|
+
export declare function parseBuildManifestArgs(argv: readonly string[]): BuildManifestArgs;
|