@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
package/dist/src/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { PKG_VERSION } from './version.js';
|
|
|
18
18
|
import { printCompletionScript } from './completion.js';
|
|
19
19
|
import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled } from './external.js';
|
|
20
20
|
import { registerAllCommands } from './commanderAdapter.js';
|
|
21
|
+
import { formatRootAdapterHelpText, installStructuredHelp, rootHelpData } from './help.js';
|
|
21
22
|
import { EXIT_CODES, getErrorMessage, BrowserConnectError } from './errors.js';
|
|
22
23
|
import { TargetError } from './browser/target-errors.js';
|
|
23
24
|
import { resolveTargetJs, getTextResolvedJs, getValueResolvedJs, getAttributesResolvedJs, selectResolvedJs, isAutocompleteResolvedJs } from './browser/target-resolver.js';
|
|
@@ -392,6 +393,20 @@ function applyVerbose(opts) {
|
|
|
392
393
|
if (opts.verbose)
|
|
393
394
|
process.env.OPENCLI_VERBOSE = '1';
|
|
394
395
|
}
|
|
396
|
+
function formatChildCommandSummary(command) {
|
|
397
|
+
return [...new Set(command.commands.map(child => child.name()))]
|
|
398
|
+
.sort((a, b) => a.localeCompare(b))
|
|
399
|
+
.join(', ');
|
|
400
|
+
}
|
|
401
|
+
function applyRootSubcommandSummaries(program) {
|
|
402
|
+
for (const command of program.commands) {
|
|
403
|
+
if (command.commands.length === 0)
|
|
404
|
+
continue;
|
|
405
|
+
const summary = formatChildCommandSummary(command);
|
|
406
|
+
if (summary)
|
|
407
|
+
command.description(summary);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
395
410
|
export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
396
411
|
const program = new Command();
|
|
397
412
|
// enablePositionalOptions: prevents parent from consuming flags meant for subcommands;
|
|
@@ -421,13 +436,14 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
421
436
|
name: c.name,
|
|
422
437
|
aliases: c.aliases?.join(', ') ?? '',
|
|
423
438
|
description: c.description,
|
|
439
|
+
access: c.access,
|
|
424
440
|
strategy: strategyLabel(c),
|
|
425
441
|
browser: !!c.browser,
|
|
426
442
|
args: formatArgSummary(c.args),
|
|
427
443
|
}));
|
|
428
444
|
renderOutput(rows, {
|
|
429
445
|
fmt,
|
|
430
|
-
columns: ['command', 'site', 'name', 'aliases', 'description', 'strategy', 'browser', 'args',
|
|
446
|
+
columns: ['command', 'site', 'name', 'aliases', 'description', 'access', 'strategy', 'browser', 'args',
|
|
431
447
|
...(isStructured ? ['columns', 'domain'] : [])],
|
|
432
448
|
title: 'opencli/list',
|
|
433
449
|
source: 'opencli list',
|
|
@@ -489,6 +505,30 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
489
505
|
console.log(renderVerifyReport(r));
|
|
490
506
|
process.exitCode = r.ok ? EXIT_CODES.SUCCESS : EXIT_CODES.GENERIC_ERROR;
|
|
491
507
|
});
|
|
508
|
+
program
|
|
509
|
+
.command('convention-audit')
|
|
510
|
+
.description('Scan adapters for agent-native convention violations')
|
|
511
|
+
.argument('[target]', 'site or site/name')
|
|
512
|
+
.option('--site <site>', 'Limit audit to one site')
|
|
513
|
+
.option('-f, --format <fmt>', 'Output format: table, json, yaml', 'table')
|
|
514
|
+
.option('--strict', 'Exit non-zero when violations are found', false)
|
|
515
|
+
.action(async (target, opts) => {
|
|
516
|
+
const { runConventionAudit, renderConventionAuditText } = await import('./convention-audit.js');
|
|
517
|
+
const report = runConventionAudit({
|
|
518
|
+
projectRoot: findPackageRoot(CLI_FILE),
|
|
519
|
+
target,
|
|
520
|
+
site: opts.site,
|
|
521
|
+
});
|
|
522
|
+
const fmt = String(opts.format ?? 'table').toLowerCase();
|
|
523
|
+
if (fmt === 'json' || fmt === 'yaml' || fmt === 'yml') {
|
|
524
|
+
renderOutput(report, { fmt });
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
console.log(renderConventionAuditText(report));
|
|
528
|
+
}
|
|
529
|
+
if (opts.strict && !report.ok)
|
|
530
|
+
process.exitCode = EXIT_CODES.GENERIC_ERROR;
|
|
531
|
+
});
|
|
492
532
|
// ── Built-in: browser (browser control for Claude Code skill) ───────────────
|
|
493
533
|
//
|
|
494
534
|
// Make websites accessible for AI agents.
|
|
@@ -542,6 +582,19 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
542
582
|
},
|
|
543
583
|
}, null, 2));
|
|
544
584
|
}
|
|
585
|
+
function isJavaScriptDialogMessage(message) {
|
|
586
|
+
const normalized = message.toLowerCase();
|
|
587
|
+
return normalized.includes('javascript dialog');
|
|
588
|
+
}
|
|
589
|
+
function emitJavaScriptDialogError(message) {
|
|
590
|
+
console.log(JSON.stringify({
|
|
591
|
+
error: {
|
|
592
|
+
code: 'javascript_dialog_open',
|
|
593
|
+
message,
|
|
594
|
+
hint: 'Handle the modal first: opencli browser dialog accept (or dismiss). Use --text for prompt dialogs.',
|
|
595
|
+
},
|
|
596
|
+
}, null, 2));
|
|
597
|
+
}
|
|
545
598
|
/** Wrap browser actions with error handling and optional --json output */
|
|
546
599
|
function browserAction(fn) {
|
|
547
600
|
return async (...args) => {
|
|
@@ -560,7 +613,10 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
560
613
|
log.error(`Hint: ${err.hint}`);
|
|
561
614
|
}
|
|
562
615
|
else if (err instanceof BrowserCommandError) {
|
|
563
|
-
if (err.
|
|
616
|
+
if (isJavaScriptDialogMessage(err.message)) {
|
|
617
|
+
emitJavaScriptDialogError(err.message);
|
|
618
|
+
}
|
|
619
|
+
else if (err.code) {
|
|
564
620
|
console.log(JSON.stringify({
|
|
565
621
|
error: {
|
|
566
622
|
code: err.code,
|
|
@@ -582,7 +638,11 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
582
638
|
}
|
|
583
639
|
else {
|
|
584
640
|
const msg = getErrorMessage(err);
|
|
585
|
-
if (
|
|
641
|
+
if (isJavaScriptDialogMessage(msg)) {
|
|
642
|
+
emitJavaScriptDialogError(msg);
|
|
643
|
+
log.error(msg);
|
|
644
|
+
}
|
|
645
|
+
else if (msg.includes('attach failed') || msg.includes('chrome-extension://')) {
|
|
586
646
|
log.error(`Browser attach failed — another extension may be interfering. Try disabling 1Password.`);
|
|
587
647
|
}
|
|
588
648
|
else {
|
|
@@ -1295,6 +1355,60 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
1295
1355
|
await page.pressKey(key);
|
|
1296
1356
|
console.log(`Pressed: ${key}`);
|
|
1297
1357
|
}));
|
|
1358
|
+
const browserDialog = browser
|
|
1359
|
+
.command('dialog')
|
|
1360
|
+
.description('Handle a blocking JavaScript alert/confirm/prompt dialog');
|
|
1361
|
+
addBrowserTabOption(browserDialog.command('accept')
|
|
1362
|
+
.option('--text <text>', 'Prompt text to submit for prompt() dialogs')
|
|
1363
|
+
.description('Accept the currently open JavaScript dialog'))
|
|
1364
|
+
.action(browserAction(async (page, opts) => {
|
|
1365
|
+
if (!page.handleJavaScriptDialog) {
|
|
1366
|
+
throw new Error('This browser session does not support JavaScript dialog handling');
|
|
1367
|
+
}
|
|
1368
|
+
try {
|
|
1369
|
+
await page.handleJavaScriptDialog(true, opts?.text);
|
|
1370
|
+
}
|
|
1371
|
+
catch (err) {
|
|
1372
|
+
const message = getErrorMessage(err);
|
|
1373
|
+
if (message.toLowerCase().includes('no dialog')) {
|
|
1374
|
+
console.log(JSON.stringify({
|
|
1375
|
+
error: {
|
|
1376
|
+
code: 'no_javascript_dialog',
|
|
1377
|
+
message: 'No JavaScript dialog is currently open.',
|
|
1378
|
+
},
|
|
1379
|
+
}, null, 2));
|
|
1380
|
+
process.exitCode = EXIT_CODES.USAGE_ERROR;
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
throw err;
|
|
1384
|
+
}
|
|
1385
|
+
console.log(JSON.stringify({ handled: true, action: 'accept', ...(opts?.text !== undefined && { text: opts.text }) }, null, 2));
|
|
1386
|
+
}));
|
|
1387
|
+
addBrowserTabOption(browserDialog.command('dismiss')
|
|
1388
|
+
.description('Dismiss the currently open JavaScript dialog'))
|
|
1389
|
+
.action(browserAction(async (page) => {
|
|
1390
|
+
if (!page.handleJavaScriptDialog) {
|
|
1391
|
+
throw new Error('This browser session does not support JavaScript dialog handling');
|
|
1392
|
+
}
|
|
1393
|
+
try {
|
|
1394
|
+
await page.handleJavaScriptDialog(false);
|
|
1395
|
+
}
|
|
1396
|
+
catch (err) {
|
|
1397
|
+
const message = getErrorMessage(err);
|
|
1398
|
+
if (message.toLowerCase().includes('no dialog')) {
|
|
1399
|
+
console.log(JSON.stringify({
|
|
1400
|
+
error: {
|
|
1401
|
+
code: 'no_javascript_dialog',
|
|
1402
|
+
message: 'No JavaScript dialog is currently open.',
|
|
1403
|
+
},
|
|
1404
|
+
}, null, 2));
|
|
1405
|
+
process.exitCode = EXIT_CODES.USAGE_ERROR;
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
throw err;
|
|
1409
|
+
}
|
|
1410
|
+
console.log(JSON.stringify({ handled: true, action: 'dismiss' }, null, 2));
|
|
1411
|
+
}));
|
|
1298
1412
|
// ── Wait commands ──
|
|
1299
1413
|
addBrowserTabOption(browser.command('wait'))
|
|
1300
1414
|
.argument('<type>', 'selector, text, time, or xhr')
|
|
@@ -1741,6 +1855,8 @@ cli({
|
|
|
1741
1855
|
site: '${site}',
|
|
1742
1856
|
name: '${command}',
|
|
1743
1857
|
description: '', // TODO: describe what this command does
|
|
1858
|
+
access: 'read', // TODO: 'read' for queries, 'write' for remote/account state changes
|
|
1859
|
+
example: 'opencli ${site} ${command} -f yaml',
|
|
1744
1860
|
domain: '${domain}',
|
|
1745
1861
|
strategy: Strategy.PUBLIC, // TODO: PUBLIC (no auth), COOKIE (needs login), UI (DOM interaction)
|
|
1746
1862
|
browser: false, // TODO: set true if needs browser
|
|
@@ -1759,6 +1875,7 @@ cli({
|
|
|
1759
1875
|
fs.mkdirSync(dir, { recursive: true });
|
|
1760
1876
|
fs.writeFileSync(filePath, template, 'utf-8');
|
|
1761
1877
|
console.log(`Created: ${filePath}`);
|
|
1878
|
+
console.log('First time on this site? Run: opencli browser analyze <url>');
|
|
1762
1879
|
console.log(`Edit the file to implement your adapter, then run: opencli browser verify ${name}`);
|
|
1763
1880
|
}
|
|
1764
1881
|
catch (err) {
|
|
@@ -1773,6 +1890,7 @@ cli({
|
|
|
1773
1890
|
.option('--update-fixture', 'Overwrite an existing fixture with one derived from current output')
|
|
1774
1891
|
.option('--no-fixture', 'Ignore any fixture file for this run (no value-level validation)')
|
|
1775
1892
|
.option('--strict-memory', 'Fail (not just warn) when ~/.opencli/sites/<site>/endpoints.json or notes.md is missing')
|
|
1893
|
+
.option('--seed-args <value>', 'Seed args when no fixture exists; use JSON array/object for multiple args or flags')
|
|
1776
1894
|
.option('--trace <mode>', 'Trace capture for the adapter subprocess: off, on, retain-on-failure', 'off')
|
|
1777
1895
|
.description('Execute an adapter and validate output; uses fixture at ~/.opencli/sites/<site>/verify/<cmd>.json when present')
|
|
1778
1896
|
.action(async (name, opts = {}) => {
|
|
@@ -1790,7 +1908,7 @@ cli({
|
|
|
1790
1908
|
return;
|
|
1791
1909
|
}
|
|
1792
1910
|
const { execFileSync } = await import('node:child_process');
|
|
1793
|
-
const { loadFixture, writeFixture, deriveFixture, validateRows, fixturePath, expandFixtureArgs } = await import('./browser/verify-fixture.js');
|
|
1911
|
+
const { loadFixture, writeFixture, deriveFixture, validateRows, validateRowShape, fixturePath, expandFixtureArgs, parseSeedArgs } = await import('./browser/verify-fixture.js');
|
|
1794
1912
|
const filePath = path.join(os.homedir(), '.opencli', 'clis', site, `${command}.js`);
|
|
1795
1913
|
if (!fs.existsSync(filePath)) {
|
|
1796
1914
|
console.error(`Adapter not found: ${filePath}`);
|
|
@@ -1807,9 +1925,10 @@ cli({
|
|
|
1807
1925
|
// - array form ["123", "--limit", "3"] → verbatim (for positional subjects)
|
|
1808
1926
|
const adapterSrc = fs.readFileSync(filePath, 'utf-8');
|
|
1809
1927
|
const hasLimitArg = /['"]limit['"]/.test(adapterSrc);
|
|
1810
|
-
const
|
|
1811
|
-
const
|
|
1812
|
-
|
|
1928
|
+
const seedArgs = parseSeedArgs(opts.seedArgs);
|
|
1929
|
+
const explicitArgs = fixture?.args ?? seedArgs;
|
|
1930
|
+
const cliArgs = expandFixtureArgs(explicitArgs);
|
|
1931
|
+
if (explicitArgs === undefined && cliArgs.length === 0 && hasLimitArg)
|
|
1813
1932
|
cliArgs.push('--limit', '3');
|
|
1814
1933
|
const traceArgs = opts.trace && opts.trace !== 'off' ? ['--trace', opts.trace] : [];
|
|
1815
1934
|
const argDisplay = [...cliArgs, ...traceArgs].join(' ');
|
|
@@ -1851,6 +1970,20 @@ cli({
|
|
|
1851
1970
|
}
|
|
1852
1971
|
console.log(renderVerifyPreview(rows));
|
|
1853
1972
|
console.log(`\n → ${rows.length} row${rows.length === 1 ? '' : 's'}`);
|
|
1973
|
+
const shapeFailures = validateRowShape(rows);
|
|
1974
|
+
if (shapeFailures.length > 0) {
|
|
1975
|
+
console.log(`\n ✗ Adapter output violates row shape conventions:`);
|
|
1976
|
+
for (const f of shapeFailures.slice(0, 20)) {
|
|
1977
|
+
const where = f.rowIndex !== undefined ? `row[${f.rowIndex}] ` : '';
|
|
1978
|
+
console.log(` - [${f.rule}] ${where}${f.detail}`);
|
|
1979
|
+
}
|
|
1980
|
+
if (shapeFailures.length > 20) {
|
|
1981
|
+
console.log(` ... and ${shapeFailures.length - 20} more failure(s)`);
|
|
1982
|
+
}
|
|
1983
|
+
console.log(`\n Keep rows agent-native: <=12 top-level keys, nesting depth <=1, and id-shaped fields at top level.`);
|
|
1984
|
+
process.exitCode = EXIT_CODES.GENERIC_ERROR;
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1854
1987
|
// ── Fixture handling ───────────────────────────────────────────
|
|
1855
1988
|
if (opts.writeFixture || opts.updateFixture) {
|
|
1856
1989
|
if (fixture && !opts.updateFixture) {
|
|
@@ -1858,10 +1991,10 @@ cli({
|
|
|
1858
1991
|
console.log(` Use --update-fixture to overwrite.`);
|
|
1859
1992
|
}
|
|
1860
1993
|
else {
|
|
1861
|
-
const
|
|
1862
|
-
?
|
|
1994
|
+
const fixtureArgs = explicitArgs !== undefined
|
|
1995
|
+
? explicitArgs
|
|
1863
1996
|
: (hasLimitArg ? { limit: 3 } : undefined);
|
|
1864
|
-
const derived = deriveFixture(rows,
|
|
1997
|
+
const derived = deriveFixture(rows, fixtureArgs);
|
|
1865
1998
|
const p = writeFixture(site, command, derived);
|
|
1866
1999
|
console.log(`\n ${fixture ? '↻ Updated' : '✎ Wrote'} fixture: ${p}`);
|
|
1867
2000
|
console.log(` Review and hand-tune the derived expectations (add patterns / notEmpty, tighten rowCount).`);
|
|
@@ -2404,7 +2537,13 @@ cli({
|
|
|
2404
2537
|
// ── Dynamic adapter commands ──────────────────────────────────────────────
|
|
2405
2538
|
const siteGroups = new Map();
|
|
2406
2539
|
siteGroups.set('antigravity', antigravityCmd);
|
|
2407
|
-
registerAllCommands(program, siteGroups);
|
|
2540
|
+
const siteNames = registerAllCommands(program, siteGroups);
|
|
2541
|
+
applyRootSubcommandSummaries(program);
|
|
2542
|
+
const siteNameSet = new Set(siteNames);
|
|
2543
|
+
program.configureHelp({
|
|
2544
|
+
visibleCommands: (command) => command.commands.filter(child => command !== program || !siteNameSet.has(child.name())),
|
|
2545
|
+
});
|
|
2546
|
+
installStructuredHelp(program, () => rootHelpData(program, siteNames), () => formatRootAdapterHelpText(siteNames));
|
|
2408
2547
|
// ── Unknown command fallback ──────────────────────────────────────────────
|
|
2409
2548
|
// Security: do NOT auto-discover and register arbitrary system binaries.
|
|
2410
2549
|
// Only explicitly registered external CLIs are allowed.
|
package/dist/src/cli.test.js
CHANGED
|
@@ -2,6 +2,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
|
+
import yaml from 'js-yaml';
|
|
6
|
+
import { cli, getRegistry, Strategy } from './registry.js';
|
|
5
7
|
import { BrowserCommandError } from './browser/daemon-client.js';
|
|
6
8
|
import { TargetError } from './browser/target-errors.js';
|
|
7
9
|
import { PKG_VERSION } from './version.js';
|
|
@@ -38,6 +40,130 @@ vi.mock('node:child_process', async () => {
|
|
|
38
40
|
};
|
|
39
41
|
});
|
|
40
42
|
import { createProgram, findPackageRoot, normalizeVerifyRows, renderVerifyPreview, resolveBrowserVerifyInvocation, selectFreshByTimestamp } from './cli.js';
|
|
43
|
+
describe('createProgram root help descriptions', () => {
|
|
44
|
+
function descriptionFor(program, name) {
|
|
45
|
+
return program.commands.find(cmd => cmd.name() === name)?.description();
|
|
46
|
+
}
|
|
47
|
+
it('summarizes built-in command groups with their subcommands', () => {
|
|
48
|
+
const program = createProgram('', '');
|
|
49
|
+
expect(descriptionFor(program, 'browser')).toContain('open');
|
|
50
|
+
expect(descriptionFor(program, 'browser')).toContain('type');
|
|
51
|
+
expect(descriptionFor(program, 'browser')).toContain('verify');
|
|
52
|
+
expect(descriptionFor(program, 'browser')).not.toContain('Browser control');
|
|
53
|
+
expect(descriptionFor(program, 'plugin')).toBe('create, install, list, uninstall, update');
|
|
54
|
+
expect(descriptionFor(program, 'adapter')).toBe('eject, reset, status');
|
|
55
|
+
expect(descriptionFor(program, 'profile')).toBe('list, rename, use');
|
|
56
|
+
expect(descriptionFor(program, 'daemon')).toBe('restart, status, stop');
|
|
57
|
+
expect(descriptionFor(program, 'external')).toBe('install, list, register');
|
|
58
|
+
});
|
|
59
|
+
it('keeps leaf command descriptions unchanged', () => {
|
|
60
|
+
const program = createProgram('', '');
|
|
61
|
+
expect(descriptionFor(program, 'list')).toBe('List all available CLI commands');
|
|
62
|
+
expect(descriptionFor(program, 'doctor')).toBe('Diagnose opencli browser bridge connectivity');
|
|
63
|
+
});
|
|
64
|
+
it('keeps site adapters out of root commands and lists sites in the root help tail', () => {
|
|
65
|
+
const registry = getRegistry();
|
|
66
|
+
const snapshot = new Map(registry);
|
|
67
|
+
registry.clear();
|
|
68
|
+
try {
|
|
69
|
+
cli({
|
|
70
|
+
site: 'bilibili',
|
|
71
|
+
name: 'hot',
|
|
72
|
+
access: 'read',
|
|
73
|
+
description: 'Bilibili hot videos',
|
|
74
|
+
strategy: Strategy.PUBLIC,
|
|
75
|
+
browser: false,
|
|
76
|
+
});
|
|
77
|
+
cli({
|
|
78
|
+
site: 'youtube',
|
|
79
|
+
name: 'search',
|
|
80
|
+
access: 'read',
|
|
81
|
+
description: 'Search YouTube',
|
|
82
|
+
strategy: Strategy.PUBLIC,
|
|
83
|
+
browser: false,
|
|
84
|
+
});
|
|
85
|
+
const program = createProgram('', '');
|
|
86
|
+
const help = program.helpInformation();
|
|
87
|
+
expect(help).toContain('Site adapters (2):');
|
|
88
|
+
expect(help).toContain('bilibili, youtube');
|
|
89
|
+
expect(help).toContain("opencli <site> --help -f yaml");
|
|
90
|
+
expect(help).not.toMatch(/\n bilibili\s+hot/);
|
|
91
|
+
expect(help).not.toMatch(/\n youtube\s+search/);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
registry.clear();
|
|
95
|
+
for (const [key, value] of snapshot)
|
|
96
|
+
registry.set(key, value);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
it('renders root structured help with built-ins and site adapter names', () => {
|
|
100
|
+
const registry = getRegistry();
|
|
101
|
+
const snapshot = new Map(registry);
|
|
102
|
+
const argv = process.argv;
|
|
103
|
+
registry.clear();
|
|
104
|
+
try {
|
|
105
|
+
cli({
|
|
106
|
+
site: 'bilibili',
|
|
107
|
+
name: 'hot',
|
|
108
|
+
access: 'read',
|
|
109
|
+
description: 'Bilibili hot videos',
|
|
110
|
+
strategy: Strategy.PUBLIC,
|
|
111
|
+
browser: false,
|
|
112
|
+
});
|
|
113
|
+
const program = createProgram('', '');
|
|
114
|
+
process.argv = ['node', 'opencli', '--help', '-f', 'yaml'];
|
|
115
|
+
const data = yaml.load(program.helpInformation());
|
|
116
|
+
expect(data.site_adapters.count).toBe(1);
|
|
117
|
+
expect(data.site_adapters.sites).toEqual(['bilibili']);
|
|
118
|
+
expect(data.commands.map((cmd) => cmd.name)).toContain('list');
|
|
119
|
+
expect(data.commands.map((cmd) => cmd.name)).not.toContain('bilibili');
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
process.argv = argv;
|
|
123
|
+
registry.clear();
|
|
124
|
+
for (const [key, value] of snapshot)
|
|
125
|
+
registry.set(key, value);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
it('renders per-site structured help with all commands, access, args, and examples', () => {
|
|
129
|
+
const registry = getRegistry();
|
|
130
|
+
const snapshot = new Map(registry);
|
|
131
|
+
const argv = process.argv;
|
|
132
|
+
registry.clear();
|
|
133
|
+
try {
|
|
134
|
+
cli({
|
|
135
|
+
site: 'bilibili',
|
|
136
|
+
name: 'hot',
|
|
137
|
+
access: 'read',
|
|
138
|
+
description: 'Bilibili hot videos',
|
|
139
|
+
strategy: Strategy.PUBLIC,
|
|
140
|
+
browser: false,
|
|
141
|
+
args: [{ name: 'limit', type: 'int', default: 20, help: 'Number of videos' }],
|
|
142
|
+
});
|
|
143
|
+
const program = createProgram('', '');
|
|
144
|
+
const site = program.commands.find(cmd => cmd.name() === 'bilibili');
|
|
145
|
+
expect(site).toBeTruthy();
|
|
146
|
+
process.argv = ['node', 'opencli', 'bilibili', '--help', '-f', 'yaml'];
|
|
147
|
+
const data = yaml.load(site.helpInformation());
|
|
148
|
+
expect(data.site).toBe('bilibili');
|
|
149
|
+
expect(data.commands).toMatchObject([
|
|
150
|
+
{
|
|
151
|
+
name: 'hot',
|
|
152
|
+
access: 'read',
|
|
153
|
+
description: 'Bilibili hot videos',
|
|
154
|
+
example: 'opencli bilibili hot -f yaml',
|
|
155
|
+
args: [{ name: 'limit', type: 'int', default: 20 }],
|
|
156
|
+
},
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
process.argv = argv;
|
|
161
|
+
registry.clear();
|
|
162
|
+
for (const [key, value] of snapshot)
|
|
163
|
+
registry.set(key, value);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
41
167
|
describe('resolveBrowserVerifyInvocation', () => {
|
|
42
168
|
it('prefers the built entry declared in package metadata', () => {
|
|
43
169
|
const projectRoot = path.join('repo-root');
|
|
@@ -151,6 +277,97 @@ describe('browser verify', () => {
|
|
|
151
277
|
fs.rmSync(fakeHome, { recursive: true, force: true });
|
|
152
278
|
}
|
|
153
279
|
});
|
|
280
|
+
it('uses --seed-args when no fixture args exist', async () => {
|
|
281
|
+
const originalHome = process.env.HOME;
|
|
282
|
+
const originalUserProfile = process.env.USERPROFILE;
|
|
283
|
+
const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-browser-verify-seed-'));
|
|
284
|
+
process.env.HOME = fakeHome;
|
|
285
|
+
process.env.USERPROFILE = fakeHome;
|
|
286
|
+
try {
|
|
287
|
+
const adapterDir = path.join(fakeHome, '.opencli', 'clis', 'hn');
|
|
288
|
+
fs.mkdirSync(adapterDir, { recursive: true });
|
|
289
|
+
fs.writeFileSync(path.join(adapterDir, 'top.js'), 'export default {};\n', 'utf-8');
|
|
290
|
+
const program = createProgram('', '');
|
|
291
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'verify', 'hn/top', '--no-fixture', '--seed-args', 'opencli-verify']);
|
|
292
|
+
expect(mockExecFileSync).toHaveBeenCalledTimes(1);
|
|
293
|
+
const [, execArgs] = mockExecFileSync.mock.calls[0];
|
|
294
|
+
expect(execArgs.slice(-5)).toEqual(['hn', 'top', 'opencli-verify', '--format', 'json']);
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
if (originalHome === undefined)
|
|
298
|
+
delete process.env.HOME;
|
|
299
|
+
else
|
|
300
|
+
process.env.HOME = originalHome;
|
|
301
|
+
if (originalUserProfile === undefined)
|
|
302
|
+
delete process.env.USERPROFILE;
|
|
303
|
+
else
|
|
304
|
+
process.env.USERPROFILE = originalUserProfile;
|
|
305
|
+
fs.rmSync(fakeHome, { recursive: true, force: true });
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
it('writes --seed-args into a starter fixture', async () => {
|
|
309
|
+
const originalHome = process.env.HOME;
|
|
310
|
+
const originalUserProfile = process.env.USERPROFILE;
|
|
311
|
+
const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-browser-verify-write-seed-'));
|
|
312
|
+
process.env.HOME = fakeHome;
|
|
313
|
+
process.env.USERPROFILE = fakeHome;
|
|
314
|
+
mockExecFileSync.mockReturnValue(JSON.stringify([{ title: 'ok' }]));
|
|
315
|
+
try {
|
|
316
|
+
const adapterDir = path.join(fakeHome, '.opencli', 'clis', 'hn');
|
|
317
|
+
fs.mkdirSync(adapterDir, { recursive: true });
|
|
318
|
+
fs.writeFileSync(path.join(adapterDir, 'top.js'), 'export default {};\n', 'utf-8');
|
|
319
|
+
const program = createProgram('', '');
|
|
320
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'verify', 'hn/top', '--write-fixture', '--seed-args', 'opencli-verify']);
|
|
321
|
+
const fixtureFile = path.join(fakeHome, '.opencli', 'sites', 'hn', 'verify', 'top.json');
|
|
322
|
+
const fixture = JSON.parse(fs.readFileSync(fixtureFile, 'utf-8'));
|
|
323
|
+
expect(fixture.args).toEqual(['opencli-verify']);
|
|
324
|
+
expect(fixture.expect.columns).toEqual(['title']);
|
|
325
|
+
}
|
|
326
|
+
finally {
|
|
327
|
+
if (originalHome === undefined)
|
|
328
|
+
delete process.env.HOME;
|
|
329
|
+
else
|
|
330
|
+
process.env.HOME = originalHome;
|
|
331
|
+
if (originalUserProfile === undefined)
|
|
332
|
+
delete process.env.USERPROFILE;
|
|
333
|
+
else
|
|
334
|
+
process.env.USERPROFILE = originalUserProfile;
|
|
335
|
+
fs.rmSync(fakeHome, { recursive: true, force: true });
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
it('fails before fixture handling when output row shape is not agent-native', async () => {
|
|
339
|
+
const originalHome = process.env.HOME;
|
|
340
|
+
const originalUserProfile = process.env.USERPROFILE;
|
|
341
|
+
const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-browser-verify-shape-'));
|
|
342
|
+
process.env.HOME = fakeHome;
|
|
343
|
+
process.env.USERPROFILE = fakeHome;
|
|
344
|
+
mockExecFileSync.mockReturnValue(JSON.stringify([{ title: 'ok', author: { user_id: 'u1' } }]));
|
|
345
|
+
const consoleLogSpy = vi.mocked(console.log);
|
|
346
|
+
consoleLogSpy.mockClear();
|
|
347
|
+
try {
|
|
348
|
+
const adapterDir = path.join(fakeHome, '.opencli', 'clis', 'hn');
|
|
349
|
+
fs.mkdirSync(adapterDir, { recursive: true });
|
|
350
|
+
fs.writeFileSync(path.join(adapterDir, 'top.js'), 'export default {};\n', 'utf-8');
|
|
351
|
+
const program = createProgram('', '');
|
|
352
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'verify', 'hn/top', '--no-fixture']);
|
|
353
|
+
expect(process.exitCode).toBe(1);
|
|
354
|
+
const output = consoleLogSpy.mock.calls.map((args) => args.join(' ')).join('\n');
|
|
355
|
+
expect(output).toContain('Adapter output violates row shape conventions');
|
|
356
|
+
expect(output).toContain('author.user_id');
|
|
357
|
+
}
|
|
358
|
+
finally {
|
|
359
|
+
consoleLogSpy.mockClear();
|
|
360
|
+
if (originalHome === undefined)
|
|
361
|
+
delete process.env.HOME;
|
|
362
|
+
else
|
|
363
|
+
process.env.HOME = originalHome;
|
|
364
|
+
if (originalUserProfile === undefined)
|
|
365
|
+
delete process.env.USERPROFILE;
|
|
366
|
+
else
|
|
367
|
+
process.env.USERPROFILE = originalUserProfile;
|
|
368
|
+
fs.rmSync(fakeHome, { recursive: true, force: true });
|
|
369
|
+
}
|
|
370
|
+
});
|
|
154
371
|
});
|
|
155
372
|
describe('profile list', () => {
|
|
156
373
|
const stdoutSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
@@ -240,6 +457,7 @@ describe('browser tab targeting commands', () => {
|
|
|
240
457
|
selectTab: vi.fn().mockResolvedValue(undefined),
|
|
241
458
|
newTab: vi.fn().mockResolvedValue('tab-3'),
|
|
242
459
|
closeTab: vi.fn().mockResolvedValue(undefined),
|
|
460
|
+
handleJavaScriptDialog: vi.fn().mockResolvedValue(undefined),
|
|
243
461
|
frames: vi.fn().mockResolvedValue([
|
|
244
462
|
{ index: 0, frameId: 'frame-1', url: 'https://x.example/embed', name: 'x-embed' },
|
|
245
463
|
]),
|
|
@@ -312,6 +530,25 @@ describe('browser tab targeting commands', () => {
|
|
|
312
530
|
expect(out.error.code).toBe('bound_session_missing');
|
|
313
531
|
expect(process.exitCode).toBeDefined();
|
|
314
532
|
});
|
|
533
|
+
it('accepts JavaScript dialogs through the browser dialog command', async () => {
|
|
534
|
+
const program = createProgram('', '');
|
|
535
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'dialog', 'accept', '--text', 'ok']);
|
|
536
|
+
expect(browserState.page?.handleJavaScriptDialog).toHaveBeenCalledWith(true, 'ok');
|
|
537
|
+
const out = lastJsonLog();
|
|
538
|
+
expect(out).toEqual({ handled: true, action: 'accept', text: 'ok' });
|
|
539
|
+
});
|
|
540
|
+
it('emits a structured error when a browser action is blocked by a JavaScript dialog', async () => {
|
|
541
|
+
browserState.page = {
|
|
542
|
+
...browserState.page,
|
|
543
|
+
evaluate: vi.fn().mockRejectedValue(new Error('JavaScript dialog showing')),
|
|
544
|
+
};
|
|
545
|
+
const program = createProgram('', '');
|
|
546
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'eval', 'document.title']);
|
|
547
|
+
const out = lastJsonLog();
|
|
548
|
+
expect(out.error.code).toBe('javascript_dialog_open');
|
|
549
|
+
expect(out.error.hint).toContain('browser dialog accept');
|
|
550
|
+
expect(process.exitCode).toBeDefined();
|
|
551
|
+
});
|
|
315
552
|
it('binds browser commands to an explicit target tab via --tab', async () => {
|
|
316
553
|
const program = createProgram('', '');
|
|
317
554
|
await program.parseAsync(['node', 'opencli', 'browser', 'eval', '--tab', 'tab-2', 'document.title']);
|
|
@@ -18,4 +18,4 @@ export declare function registerCommandToProgram(siteCmd: Command, cmd: CliComma
|
|
|
18
18
|
/**
|
|
19
19
|
* Register all commands from the registry onto a Commander program.
|
|
20
20
|
*/
|
|
21
|
-
export declare function registerAllCommands(program: Command, siteGroups: Map<string, Command>):
|
|
21
|
+
export declare function registerAllCommands(program: Command, siteGroups: Map<string, Command>): string[];
|
|
@@ -15,6 +15,7 @@ import { fullName, getRegistry } from './registry.js';
|
|
|
15
15
|
import { formatRegistryHelpText } from './serialization.js';
|
|
16
16
|
import { render as renderOutput } from './output.js';
|
|
17
17
|
import { executeCommand, prepareCommandArgs } from './execution.js';
|
|
18
|
+
import { commandHelpData, formatSiteCommandDescription, installStructuredHelp, siteHelpData, } from './help.js';
|
|
18
19
|
import { CliError, EXIT_CODES, toEnvelope, } from './errors.js';
|
|
19
20
|
/**
|
|
20
21
|
* Register a single CliCommand as a Commander subcommand.
|
|
@@ -22,8 +23,7 @@ import { CliError, EXIT_CODES, toEnvelope, } from './errors.js';
|
|
|
22
23
|
export function registerCommandToProgram(siteCmd, cmd) {
|
|
23
24
|
if (siteCmd.commands.some((c) => c.name() === cmd.name))
|
|
24
25
|
return;
|
|
25
|
-
const
|
|
26
|
-
const subCmd = siteCmd.command(cmd.name).description(`${cmd.description}${deprecatedSuffix}`);
|
|
26
|
+
const subCmd = siteCmd.command(cmd.name).description(formatSiteCommandDescription(cmd));
|
|
27
27
|
if (cmd.aliases?.length)
|
|
28
28
|
subCmd.aliases(cmd.aliases);
|
|
29
29
|
// Register positional args first, then named options
|
|
@@ -49,7 +49,7 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
49
49
|
.option('-f, --format <fmt>', 'Output format: table, plain, json, yaml, md, csv', 'table')
|
|
50
50
|
.option('--trace <mode>', 'Trace capture: off, on, retain-on-failure', 'off')
|
|
51
51
|
.option('-v, --verbose', 'Debug output', false);
|
|
52
|
-
subCmd
|
|
52
|
+
installStructuredHelp(subCmd, () => commandHelpData(cmd), () => formatRegistryHelpText(cmd));
|
|
53
53
|
subCmd.action(async (...actionArgs) => {
|
|
54
54
|
const actionOpts = actionArgs[positionalArgs.length] ?? {};
|
|
55
55
|
const optionsRecord = typeof actionOpts === 'object' && actionOpts !== null ? actionOpts : {};
|
|
@@ -161,15 +161,25 @@ function renderError(err, cmdName, verbose, traceMode) {
|
|
|
161
161
|
*/
|
|
162
162
|
export function registerAllCommands(program, siteGroups) {
|
|
163
163
|
const seen = new Set();
|
|
164
|
+
const commandsBySite = new Map();
|
|
164
165
|
for (const [, cmd] of getRegistry()) {
|
|
165
166
|
if (seen.has(cmd))
|
|
166
167
|
continue;
|
|
167
168
|
seen.add(cmd);
|
|
168
|
-
|
|
169
|
+
const commands = commandsBySite.get(cmd.site) ?? [];
|
|
170
|
+
commands.push(cmd);
|
|
171
|
+
commandsBySite.set(cmd.site, commands);
|
|
172
|
+
}
|
|
173
|
+
for (const [site, commands] of commandsBySite) {
|
|
174
|
+
let siteCmd = siteGroups.get(site);
|
|
169
175
|
if (!siteCmd) {
|
|
170
|
-
siteCmd = program.command(
|
|
171
|
-
siteGroups.set(
|
|
176
|
+
siteCmd = program.command(site);
|
|
177
|
+
siteGroups.set(site, siteCmd);
|
|
178
|
+
}
|
|
179
|
+
for (const cmd of commands) {
|
|
180
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
172
181
|
}
|
|
173
|
-
|
|
182
|
+
installStructuredHelp(siteCmd, () => siteHelpData(site, commands));
|
|
174
183
|
}
|
|
184
|
+
return [...commandsBySite.keys()].sort((a, b) => a.localeCompare(b));
|
|
175
185
|
}
|