@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
|
@@ -5,8 +5,21 @@
|
|
|
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.
|
|
10
23
|
*/
|
|
11
24
|
import * as fs from 'node:fs';
|
|
12
25
|
import * as path from 'node:path';
|
|
@@ -14,12 +27,27 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
14
27
|
import { getErrorMessage } from './errors.js';
|
|
15
28
|
import { fullName, getRegistry } from './registry.js';
|
|
16
29
|
import { findPackageRoot, getCliManifestPath } from './package-paths.js';
|
|
30
|
+
import { isRecord } from './utils.js';
|
|
17
31
|
const PACKAGE_ROOT = findPackageRoot(fileURLToPath(import.meta.url));
|
|
18
32
|
const CLIS_DIR = path.join(PACKAGE_ROOT, 'clis');
|
|
19
33
|
// Write manifest next to clis/ so both dev and installed runtime can find it.
|
|
20
34
|
const OUTPUT = getCliManifestPath(CLIS_DIR);
|
|
21
|
-
import { isRecord } from './utils.js';
|
|
22
35
|
const CLI_MODULE_PATTERN = /\bcli\s*\(/;
|
|
36
|
+
/**
|
|
37
|
+
* Thrown by `loadManifestEntries` when an adapter file looks like a CLI
|
|
38
|
+
* module (matches CLI_MODULE_PATTERN) but cannot be imported. Callers
|
|
39
|
+
* decide whether to abort or aggregate failures across the whole scan.
|
|
40
|
+
*/
|
|
41
|
+
export class ManifestImportError extends Error {
|
|
42
|
+
filePath;
|
|
43
|
+
cause;
|
|
44
|
+
constructor(filePath, cause) {
|
|
45
|
+
super(`failed to scan ${filePath}: ${getErrorMessage(cause)}`);
|
|
46
|
+
this.filePath = filePath;
|
|
47
|
+
this.cause = cause;
|
|
48
|
+
this.name = 'ManifestImportError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
23
51
|
function toManifestArgs(args) {
|
|
24
52
|
return args.map(arg => ({
|
|
25
53
|
name: arg.name,
|
|
@@ -39,14 +67,15 @@ function toModulePath(filePath, site) {
|
|
|
39
67
|
export function normalizeManifestPath(relativePath) {
|
|
40
68
|
return relativePath.replace(/\\/g, '/');
|
|
41
69
|
}
|
|
42
|
-
function toManifestRelativePath(filePath) {
|
|
43
|
-
return normalizeManifestPath(path.relative(
|
|
70
|
+
function toManifestRelativePath(filePath, clisDir) {
|
|
71
|
+
return normalizeManifestPath(path.relative(clisDir, filePath));
|
|
44
72
|
}
|
|
45
73
|
function isCliCommandValue(value, site) {
|
|
46
74
|
return isRecord(value)
|
|
47
75
|
&& typeof value.site === 'string'
|
|
48
76
|
&& value.site === site
|
|
49
77
|
&& typeof value.name === 'string'
|
|
78
|
+
&& (value.access === 'read' || value.access === 'write')
|
|
50
79
|
&& Array.isArray(value.args);
|
|
51
80
|
}
|
|
52
81
|
function toManifestEntry(cmd, modulePath, sourceFile) {
|
|
@@ -55,6 +84,8 @@ function toManifestEntry(cmd, modulePath, sourceFile) {
|
|
|
55
84
|
name: cmd.name,
|
|
56
85
|
aliases: cmd.aliases,
|
|
57
86
|
description: cmd.description ?? '',
|
|
87
|
+
access: cmd.access,
|
|
88
|
+
example: cmd.example,
|
|
58
89
|
domain: cmd.domain,
|
|
59
90
|
strategy: (cmd.strategy ?? 'public').toString().toLowerCase(),
|
|
60
91
|
browser: cmd.browser ?? true,
|
|
@@ -69,12 +100,30 @@ function toManifestEntry(cmd, modulePath, sourceFile) {
|
|
|
69
100
|
navigateBefore: cmd.navigateBefore,
|
|
70
101
|
};
|
|
71
102
|
}
|
|
72
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Load all manifest entries from a single adapter file.
|
|
105
|
+
*
|
|
106
|
+
* Returns `[]` for files that do not register a CLI command (helpers, types).
|
|
107
|
+
* Throws `ManifestImportError` when a file looks like a CLI module but its
|
|
108
|
+
* import or post-import processing fails — callers must decide whether to
|
|
109
|
+
* surface or aggregate the failure.
|
|
110
|
+
*
|
|
111
|
+
* The third argument `clisDir` is used to compute the POSIX-style
|
|
112
|
+
* `sourceFile` relative path; it defaults to the package's `clis/` dir so
|
|
113
|
+
* existing test callers stay backward-compatible.
|
|
114
|
+
*/
|
|
115
|
+
export async function loadManifestEntries(filePath, site, importer = moduleHref => import(moduleHref), clisDir = CLIS_DIR) {
|
|
116
|
+
let src;
|
|
117
|
+
try {
|
|
118
|
+
src = fs.readFileSync(filePath, 'utf-8');
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
throw new ManifestImportError(filePath, err);
|
|
122
|
+
}
|
|
123
|
+
// Helper / test modules that do not call cli() are not commands.
|
|
124
|
+
if (!CLI_MODULE_PATTERN.test(src))
|
|
125
|
+
return [];
|
|
73
126
|
try {
|
|
74
|
-
const src = fs.readFileSync(filePath, 'utf-8');
|
|
75
|
-
// Helper/test modules should not appear as CLI commands in the manifest.
|
|
76
|
-
if (!CLI_MODULE_PATTERN.test(src))
|
|
77
|
-
return [];
|
|
78
127
|
const modulePath = toModulePath(filePath, site);
|
|
79
128
|
const registry = getRegistry();
|
|
80
129
|
const before = new Map(registry.entries());
|
|
@@ -93,7 +142,7 @@ export async function loadManifestEntries(filePath, site, importer = moduleHref
|
|
|
93
142
|
.map(([, cmd]) => cmd);
|
|
94
143
|
// Manifest paths are cross-platform artifacts; keep them POSIX-style even
|
|
95
144
|
// when build-manifest runs on Windows.
|
|
96
|
-
const sourceRelative = toManifestRelativePath(filePath);
|
|
145
|
+
const sourceRelative = toManifestRelativePath(filePath, clisDir);
|
|
97
146
|
const seen = new Set();
|
|
98
147
|
return runtimeCommands
|
|
99
148
|
.filter((cmd) => {
|
|
@@ -107,42 +156,142 @@ export async function loadManifestEntries(filePath, site, importer = moduleHref
|
|
|
107
156
|
.map(cmd => toManifestEntry(cmd, modulePath, sourceRelative));
|
|
108
157
|
}
|
|
109
158
|
catch (err) {
|
|
110
|
-
|
|
111
|
-
process.stderr.write(`Warning: failed to scan ${filePath}: ${getErrorMessage(err)}\n`);
|
|
112
|
-
return [];
|
|
159
|
+
throw new ManifestImportError(filePath, err);
|
|
113
160
|
}
|
|
114
161
|
}
|
|
115
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Scan a `clis/` directory and aggregate per-adapter results. Import
|
|
164
|
+
* failures are collected in `failures` instead of crashing the whole scan,
|
|
165
|
+
* but the caller (e.g. `main()`) is expected to fail loud if any failure
|
|
166
|
+
* is present.
|
|
167
|
+
*/
|
|
168
|
+
export async function scanClisDir(clisDir, importer = moduleHref => import(moduleHref)) {
|
|
116
169
|
const manifest = new Map();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
170
|
+
const failures = [];
|
|
171
|
+
if (!fs.existsSync(clisDir)) {
|
|
172
|
+
return { entries: [], failures };
|
|
173
|
+
}
|
|
174
|
+
for (const site of fs.readdirSync(clisDir)) {
|
|
175
|
+
const siteDir = path.join(clisDir, site);
|
|
176
|
+
if (!fs.statSync(siteDir).isDirectory())
|
|
177
|
+
continue;
|
|
178
|
+
for (const file of fs.readdirSync(siteDir)) {
|
|
179
|
+
if (file.endsWith('.js') && !file.endsWith('.d.js') && !file.endsWith('.test.js') && file !== 'index.js') {
|
|
180
|
+
const filePath = path.join(siteDir, file);
|
|
181
|
+
try {
|
|
182
|
+
const entries = await loadManifestEntries(filePath, site, importer, clisDir);
|
|
128
183
|
for (const entry of entries) {
|
|
129
184
|
const key = `${entry.site}/${entry.name}`;
|
|
130
185
|
manifest.set(key, entry);
|
|
131
186
|
}
|
|
132
187
|
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
if (err instanceof ManifestImportError) {
|
|
190
|
+
failures.push(err);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
133
195
|
}
|
|
134
196
|
}
|
|
135
197
|
}
|
|
136
|
-
|
|
198
|
+
const entries = [...manifest.values()].sort((a, b) => a.site.localeCompare(b.site) || a.name.localeCompare(b.name));
|
|
199
|
+
return { entries, failures };
|
|
200
|
+
}
|
|
201
|
+
export async function buildManifest() {
|
|
202
|
+
return scanClisDir(CLIS_DIR);
|
|
137
203
|
}
|
|
138
204
|
export function serializeManifest(manifest) {
|
|
139
205
|
return `${JSON.stringify(manifest, null, 2)}\n`;
|
|
140
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Diff helper: returns site/name keys that exist in `prev` but not in
|
|
209
|
+
* `next`. Used as a safety net to detect accidental mass-deletions caused
|
|
210
|
+
* by silently failing adapter imports.
|
|
211
|
+
*/
|
|
212
|
+
export function diffRemovedEntries(prev, next) {
|
|
213
|
+
const nextKeys = new Set(next.map(e => `${e.site}/${e.name}`));
|
|
214
|
+
return prev
|
|
215
|
+
.map(e => `${e.site}/${e.name}`)
|
|
216
|
+
.filter(key => !nextKeys.has(key))
|
|
217
|
+
.sort();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Parse `--allow-removals` and `--allow-removals=N` from argv.
|
|
221
|
+
* Bare `--allow-removals` disables the safety net (`Infinity`); the
|
|
222
|
+
* numeric form sets an explicit upper bound.
|
|
223
|
+
*/
|
|
224
|
+
export function parseBuildManifestArgs(argv) {
|
|
225
|
+
let allowRemovals = 0;
|
|
226
|
+
for (const arg of argv) {
|
|
227
|
+
if (arg === '--allow-removals') {
|
|
228
|
+
allowRemovals = Number.POSITIVE_INFINITY;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const m = arg.match(/^--allow-removals=(\d+)$/);
|
|
232
|
+
if (m) {
|
|
233
|
+
allowRemovals = Number.parseInt(m[1], 10);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { allowRemovals };
|
|
238
|
+
}
|
|
239
|
+
function readExistingManifest(filePath) {
|
|
240
|
+
try {
|
|
241
|
+
if (!fs.existsSync(filePath))
|
|
242
|
+
return null;
|
|
243
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
244
|
+
const parsed = JSON.parse(raw);
|
|
245
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
141
251
|
async function main() {
|
|
142
|
-
|
|
252
|
+
// Runtime guard: refuse to run from dist/. tsc transitively emits this
|
|
253
|
+
// file (the test file imports from it) so dist/src/build-manifest.js
|
|
254
|
+
// physically exists. If a developer or agent runs that compiled copy,
|
|
255
|
+
// any stale dist will silently break adapter imports — the exact failure
|
|
256
|
+
// mode this script is meant to prevent. Direct them at the tsx entry
|
|
257
|
+
// before they can shoot themselves in the foot.
|
|
258
|
+
if (fileURLToPath(import.meta.url).includes(`${path.sep}dist${path.sep}`)) {
|
|
259
|
+
process.stderr.write(`❌ Refusing to run build-manifest from dist/.\n`
|
|
260
|
+
+ ` Stale compiled output silently drops adapters that import renamed/removed exports.\n`
|
|
261
|
+
+ ` Run \`npm run build-manifest\` (or \`tsx src/build-manifest.ts\`) from the source tree instead.\n`);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
const args = parseBuildManifestArgs(process.argv.slice(2));
|
|
265
|
+
const { entries, failures } = await buildManifest();
|
|
266
|
+
if (failures.length > 0) {
|
|
267
|
+
process.stderr.write(`❌ ${failures.length} adapter(s) failed to load:\n`);
|
|
268
|
+
for (const failure of failures) {
|
|
269
|
+
const rel = path.relative(PACKAGE_ROOT, failure.filePath) || failure.filePath;
|
|
270
|
+
process.stderr.write(` - ${rel}: ${getErrorMessage(failure.cause)}\n`);
|
|
271
|
+
}
|
|
272
|
+
process.stderr.write(`\nManifest NOT written. Likely cause: stale dist/ or a broken adapter import.\n`
|
|
273
|
+
+ `Always run via tsx (\`npm run build-manifest\`), not against compiled dist/.\n`);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
const existing = readExistingManifest(OUTPUT);
|
|
277
|
+
if (existing) {
|
|
278
|
+
const removed = diffRemovedEntries(existing, entries);
|
|
279
|
+
if (removed.length > args.allowRemovals) {
|
|
280
|
+
process.stderr.write(`❌ ${removed.length} manifest entries would be removed; refusing to overwrite.\n`);
|
|
281
|
+
const preview = removed.slice(0, 20);
|
|
282
|
+
for (const key of preview)
|
|
283
|
+
process.stderr.write(` - ${key}\n`);
|
|
284
|
+
if (removed.length > preview.length) {
|
|
285
|
+
process.stderr.write(` ... ${removed.length - preview.length} more\n`);
|
|
286
|
+
}
|
|
287
|
+
process.stderr.write(`\nIf this removal is intentional, rerun with `
|
|
288
|
+
+ `\`--allow-removals=${removed.length}\` (or \`--allow-removals\` to disable the check).\n`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
143
292
|
fs.mkdirSync(path.dirname(OUTPUT), { recursive: true });
|
|
144
|
-
fs.writeFileSync(OUTPUT, serializeManifest(
|
|
145
|
-
console.log(`✅ Manifest compiled: ${
|
|
293
|
+
fs.writeFileSync(OUTPUT, serializeManifest(entries));
|
|
294
|
+
console.log(`✅ Manifest compiled: ${entries.length} entries → ${OUTPUT}`);
|
|
146
295
|
// Restore executable permissions on bin entries.
|
|
147
296
|
// tsc does not preserve the +x bit, so after a clean rebuild the CLI
|
|
148
297
|
// entry-point loses its executable permission, causing "Permission denied".
|
|
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { cli, getRegistry, Strategy } from './registry.js';
|
|
6
|
-
import { loadManifestEntries, normalizeManifestPath, serializeManifest } from './build-manifest.js';
|
|
6
|
+
import { ManifestImportError, diffRemovedEntries, loadManifestEntries, normalizeManifestPath, parseBuildManifestArgs, scanClisDir, serializeManifest, } from './build-manifest.js';
|
|
7
7
|
describe('manifest helper rules', () => {
|
|
8
8
|
const tempDirs = [];
|
|
9
9
|
afterEach(() => {
|
|
@@ -24,11 +24,12 @@ describe('manifest helper rules', () => {
|
|
|
24
24
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
|
|
25
25
|
tempDirs.push(dir);
|
|
26
26
|
const file = path.join(dir, `${site}.ts`);
|
|
27
|
-
fs.writeFileSync(file, `export const command = cli({ site: '${site}', name: 'dynamic' });`);
|
|
27
|
+
fs.writeFileSync(file, `export const command = cli({ site: '${site}', name: 'dynamic', access: 'read' });`);
|
|
28
28
|
const entries = await loadManifestEntries(file, site, async () => ({
|
|
29
29
|
command: cli({
|
|
30
30
|
site,
|
|
31
31
|
name: 'dynamic',
|
|
32
|
+
access: 'read',
|
|
32
33
|
description: 'dynamic command',
|
|
33
34
|
strategy: Strategy.PUBLIC,
|
|
34
35
|
browser: false,
|
|
@@ -53,6 +54,7 @@ describe('manifest helper rules', () => {
|
|
|
53
54
|
{
|
|
54
55
|
site,
|
|
55
56
|
name: 'dynamic',
|
|
57
|
+
access: 'read',
|
|
56
58
|
description: 'dynamic command',
|
|
57
59
|
domain: 'localhost',
|
|
58
60
|
strategy: 'public',
|
|
@@ -87,11 +89,12 @@ describe('manifest helper rules', () => {
|
|
|
87
89
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
|
|
88
90
|
tempDirs.push(dir);
|
|
89
91
|
const file = path.join(dir, `${site}.ts`);
|
|
90
|
-
fs.writeFileSync(file, `cli({ site: '${site}', name: 'legacy' });`);
|
|
92
|
+
fs.writeFileSync(file, `cli({ site: '${site}', name: 'legacy', access: 'read' });`);
|
|
91
93
|
const entries = await loadManifestEntries(file, site, async () => {
|
|
92
94
|
cli({
|
|
93
95
|
site,
|
|
94
96
|
name: 'legacy',
|
|
97
|
+
access: 'read',
|
|
95
98
|
description: 'legacy command',
|
|
96
99
|
deprecated: 'legacy is deprecated',
|
|
97
100
|
replacedBy: 'opencli demo new',
|
|
@@ -102,6 +105,7 @@ describe('manifest helper rules', () => {
|
|
|
102
105
|
{
|
|
103
106
|
site,
|
|
104
107
|
name: 'legacy',
|
|
108
|
+
access: 'read',
|
|
105
109
|
description: 'legacy command',
|
|
106
110
|
strategy: 'cookie',
|
|
107
111
|
browser: true,
|
|
@@ -123,16 +127,18 @@ describe('manifest helper rules', () => {
|
|
|
123
127
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
|
|
124
128
|
tempDirs.push(dir);
|
|
125
129
|
const file = path.join(dir, `${site}.ts`);
|
|
126
|
-
fs.writeFileSync(file, `export const screen = cli({ site: '${site}', name: 'screen' });`);
|
|
130
|
+
fs.writeFileSync(file, `export const screen = cli({ site: '${site}', name: 'screen', access: 'read' });`);
|
|
127
131
|
const entries = await loadManifestEntries(file, site, async () => ({
|
|
128
132
|
screen: cli({
|
|
129
133
|
site,
|
|
130
134
|
name: 'screen',
|
|
135
|
+
access: 'read',
|
|
131
136
|
description: 'capture screen',
|
|
132
137
|
}),
|
|
133
138
|
status: cli({
|
|
134
139
|
site,
|
|
135
140
|
name: 'status',
|
|
141
|
+
access: 'read',
|
|
136
142
|
description: 'show status',
|
|
137
143
|
}),
|
|
138
144
|
}));
|
|
@@ -147,7 +153,7 @@ describe('manifest helper rules', () => {
|
|
|
147
153
|
it('serializes manifest json with a trailing newline', () => {
|
|
148
154
|
const serialized = serializeManifest([{
|
|
149
155
|
site: 'demo',
|
|
150
|
-
name: 'status',
|
|
156
|
+
name: 'status', access: 'read',
|
|
151
157
|
description: '',
|
|
152
158
|
strategy: 'public',
|
|
153
159
|
browser: false,
|
|
@@ -157,4 +163,78 @@ describe('manifest helper rules', () => {
|
|
|
157
163
|
expect(serialized.endsWith('\n')).toBe(true);
|
|
158
164
|
expect(serialized).toContain('\n]');
|
|
159
165
|
});
|
|
166
|
+
it('throws ManifestImportError when an adapter looks like a CLI module but fails to import', async () => {
|
|
167
|
+
// Reproduces the "stale dist drops adapters silently" incident: the file
|
|
168
|
+
// matches the cli() pattern (so it's not just a helper), but the importer
|
|
169
|
+
// throws — we want the failure surfaced, not swallowed.
|
|
170
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-fail-'));
|
|
171
|
+
tempDirs.push(dir);
|
|
172
|
+
const file = path.join(dir, 'broken.ts');
|
|
173
|
+
fs.writeFileSync(file, `export const command = cli({ site: 'demo', name: 'broken', access: 'read' });`);
|
|
174
|
+
const importer = async () => { throw new Error('boom: stale dist'); };
|
|
175
|
+
await expect(loadManifestEntries(file, 'demo', importer))
|
|
176
|
+
.rejects.toBeInstanceOf(ManifestImportError);
|
|
177
|
+
try {
|
|
178
|
+
await loadManifestEntries(file, 'demo', importer);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
expect(err).toBeInstanceOf(ManifestImportError);
|
|
182
|
+
const e = err;
|
|
183
|
+
expect(e.filePath).toBe(file);
|
|
184
|
+
expect(e.message).toContain('boom: stale dist');
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
it('still silently skips files that do not call cli() even if the importer would have thrown', async () => {
|
|
188
|
+
// The cli() pattern check happens before importing — we don't even ask
|
|
189
|
+
// the importer about helper modules, so a thrown import does not turn
|
|
190
|
+
// them into failures.
|
|
191
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-helper-'));
|
|
192
|
+
tempDirs.push(dir);
|
|
193
|
+
const file = path.join(dir, 'helper.ts');
|
|
194
|
+
fs.writeFileSync(file, `export const helper = () => 42;`);
|
|
195
|
+
const importer = async () => { throw new Error('should never be called'); };
|
|
196
|
+
await expect(loadManifestEntries(file, 'demo', importer)).resolves.toEqual([]);
|
|
197
|
+
});
|
|
198
|
+
it('scanClisDir aggregates per-adapter import failures instead of silently dropping them', async () => {
|
|
199
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-clis-'));
|
|
200
|
+
tempDirs.push(root);
|
|
201
|
+
const siteDir = path.join(root, 'demo');
|
|
202
|
+
fs.mkdirSync(siteDir);
|
|
203
|
+
fs.writeFileSync(path.join(siteDir, 'good.js'), `export const cmd = cli({ site: 'demo', name: 'good', access: 'read' });`);
|
|
204
|
+
fs.writeFileSync(path.join(siteDir, 'broken.js'), `export const cmd = cli({ site: 'demo', name: 'broken', access: 'read' });`);
|
|
205
|
+
const importer = async (href) => {
|
|
206
|
+
if (href.endsWith('broken.js'))
|
|
207
|
+
throw new Error('stale dist drops broken');
|
|
208
|
+
return { cmd: cli({ site: 'demo', name: 'good', access: 'read', description: 'ok' }) };
|
|
209
|
+
};
|
|
210
|
+
const result = await scanClisDir(root, importer);
|
|
211
|
+
expect(result.failures).toHaveLength(1);
|
|
212
|
+
expect(result.failures[0]).toBeInstanceOf(ManifestImportError);
|
|
213
|
+
expect(result.failures[0].filePath).toMatch(/broken\.js$/);
|
|
214
|
+
expect(result.failures[0].message).toContain('stale dist drops broken');
|
|
215
|
+
expect(result.entries.map(e => e.name)).toEqual(['good']);
|
|
216
|
+
getRegistry().delete('demo/good');
|
|
217
|
+
});
|
|
218
|
+
it('diffRemovedEntries returns site/name keys present only in prev', () => {
|
|
219
|
+
const prev = [
|
|
220
|
+
{ site: 'a', name: '1', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
|
|
221
|
+
{ site: 'a', name: '2', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
|
|
222
|
+
{ site: 'b', name: '3', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
|
|
223
|
+
];
|
|
224
|
+
const next = [
|
|
225
|
+
{ site: 'a', name: '1', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
|
|
226
|
+
];
|
|
227
|
+
expect(diffRemovedEntries(prev, next)).toEqual(['a/2', 'b/3']);
|
|
228
|
+
expect(diffRemovedEntries(prev, prev)).toEqual([]);
|
|
229
|
+
expect(diffRemovedEntries([], next)).toEqual([]);
|
|
230
|
+
});
|
|
231
|
+
it('parseBuildManifestArgs reads --allow-removals[=N]', () => {
|
|
232
|
+
expect(parseBuildManifestArgs([]).allowRemovals).toBe(0);
|
|
233
|
+
expect(parseBuildManifestArgs(['--allow-removals=5']).allowRemovals).toBe(5);
|
|
234
|
+
expect(parseBuildManifestArgs(['--allow-removals=0']).allowRemovals).toBe(0);
|
|
235
|
+
// Bare flag is the explicit "I know what I'm doing" escape hatch.
|
|
236
|
+
expect(parseBuildManifestArgs(['--allow-removals']).allowRemovals).toBe(Number.POSITIVE_INFINITY);
|
|
237
|
+
// Unknown flags are ignored.
|
|
238
|
+
expect(parseBuildManifestArgs(['--something-else']).allowRemovals).toBe(0);
|
|
239
|
+
});
|
|
160
240
|
});
|