@jackwener/opencli 1.6.8 → 1.6.10
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 +117 -59
- package/README.zh-CN.md +123 -79
- package/dist/clis/_shared/common.d.ts +3 -0
- package/dist/clis/_shared/common.js +22 -0
- package/dist/clis/bilibili/hot.d.ts +1 -0
- package/dist/clis/bilibili/hot.js +35 -0
- package/dist/clis/bluesky/feeds.d.ts +1 -0
- package/dist/clis/bluesky/feeds.js +27 -0
- package/dist/clis/bluesky/followers.d.ts +1 -0
- package/dist/clis/bluesky/followers.js +27 -0
- package/dist/clis/bluesky/following.d.ts +1 -0
- package/dist/clis/bluesky/following.js +27 -0
- package/dist/clis/bluesky/profile.d.ts +1 -0
- package/dist/clis/bluesky/profile.js +29 -0
- package/dist/clis/bluesky/search.d.ts +1 -0
- package/dist/clis/bluesky/search.js +28 -0
- package/dist/clis/bluesky/starter-packs.d.ts +1 -0
- package/dist/clis/bluesky/starter-packs.js +28 -0
- package/dist/clis/bluesky/thread.d.ts +1 -0
- package/dist/clis/bluesky/thread.js +30 -0
- package/dist/clis/bluesky/trending.d.ts +1 -0
- package/dist/clis/bluesky/trending.js +19 -0
- package/dist/clis/bluesky/user.d.ts +1 -0
- package/dist/clis/bluesky/user.js +33 -0
- package/dist/clis/cnki/search.d.ts +1 -0
- package/dist/clis/cnki/search.js +60 -0
- package/dist/clis/cnki/search.test.d.ts +1 -0
- package/dist/clis/cnki/search.test.js +18 -0
- package/dist/clis/devto/tag.d.ts +1 -0
- package/dist/clis/devto/tag.js +32 -0
- package/dist/clis/devto/top.d.ts +1 -0
- package/dist/clis/devto/top.js +26 -0
- package/dist/clis/devto/user.d.ts +1 -0
- package/dist/clis/devto/user.js +31 -0
- package/dist/clis/dictionary/examples.d.ts +1 -0
- package/dist/clis/dictionary/examples.js +27 -0
- package/dist/clis/dictionary/search.d.ts +1 -0
- package/dist/clis/dictionary/search.js +29 -0
- package/dist/clis/dictionary/synonyms.d.ts +1 -0
- package/dist/clis/dictionary/synonyms.js +27 -0
- package/dist/clis/douban/subject.d.ts +1 -0
- package/dist/clis/douban/subject.js +118 -0
- package/dist/clis/douban/top250.d.ts +1 -0
- package/dist/clis/douban/top250.js +67 -0
- package/dist/clis/facebook/add-friend.d.ts +1 -0
- package/dist/clis/facebook/add-friend.js +43 -0
- package/dist/clis/facebook/events.d.ts +1 -0
- package/dist/clis/facebook/events.js +40 -0
- package/dist/clis/facebook/feed.d.ts +1 -0
- package/dist/clis/facebook/feed.js +59 -0
- package/dist/clis/facebook/friends.d.ts +1 -0
- package/dist/clis/facebook/friends.js +38 -0
- package/dist/clis/facebook/groups.d.ts +1 -0
- package/dist/clis/facebook/groups.js +46 -0
- package/dist/clis/facebook/join-group.d.ts +1 -0
- package/dist/clis/facebook/join-group.js +44 -0
- package/dist/clis/facebook/memories.d.ts +1 -0
- package/dist/clis/facebook/memories.js +35 -0
- package/dist/clis/facebook/notifications.d.ts +1 -0
- package/dist/clis/facebook/notifications.js +36 -0
- package/dist/clis/facebook/profile.d.ts +1 -0
- package/dist/clis/facebook/profile.js +37 -0
- package/dist/clis/facebook/search.d.ts +1 -0
- package/dist/clis/facebook/search.js +38 -0
- package/dist/clis/facebook/search.test.d.ts +1 -1
- package/dist/clis/facebook/search.test.js +6 -9
- package/dist/clis/gitee/index.d.ts +3 -0
- package/dist/clis/gitee/index.js +3 -0
- package/dist/clis/gitee/search.d.ts +1 -0
- package/dist/clis/gitee/search.js +136 -0
- package/dist/clis/gitee/trending.d.ts +1 -0
- package/dist/clis/gitee/trending.js +567 -0
- package/dist/clis/gitee/user.d.ts +1 -0
- package/dist/clis/gitee/user.js +199 -0
- package/dist/clis/gitee/user.test.d.ts +1 -0
- package/dist/clis/gitee/user.test.js +63 -0
- package/dist/clis/hackernews/ask.d.ts +1 -0
- package/dist/clis/hackernews/ask.js +29 -0
- package/dist/clis/hackernews/best.d.ts +1 -0
- package/dist/clis/hackernews/best.js +29 -0
- package/dist/clis/hackernews/jobs.d.ts +1 -0
- package/dist/clis/hackernews/jobs.js +27 -0
- package/dist/clis/hackernews/new.d.ts +1 -0
- package/dist/clis/hackernews/new.js +29 -0
- package/dist/clis/hackernews/search.d.ts +1 -0
- package/dist/clis/hackernews/search.js +36 -0
- package/dist/clis/hackernews/show.d.ts +1 -0
- package/dist/clis/hackernews/show.js +29 -0
- package/dist/clis/hackernews/top.d.ts +1 -0
- package/dist/clis/hackernews/top.js +29 -0
- package/dist/clis/hackernews/user.d.ts +1 -0
- package/dist/clis/hackernews/user.js +22 -0
- package/dist/clis/hupu/hot.d.ts +1 -0
- package/dist/clis/hupu/hot.js +40 -0
- package/dist/clis/instagram/comment.d.ts +1 -0
- package/dist/clis/instagram/comment.js +47 -0
- package/dist/clis/instagram/explore.d.ts +1 -0
- package/dist/clis/instagram/explore.js +41 -0
- package/dist/clis/instagram/follow.d.ts +1 -0
- package/dist/clis/instagram/follow.js +43 -0
- package/dist/clis/instagram/followers.d.ts +1 -0
- package/dist/clis/instagram/followers.js +45 -0
- package/dist/clis/instagram/following.d.ts +1 -0
- package/dist/clis/instagram/following.js +45 -0
- package/dist/clis/instagram/like.d.ts +1 -0
- package/dist/clis/instagram/like.js +45 -0
- package/dist/clis/instagram/profile.d.ts +1 -0
- package/dist/clis/instagram/profile.js +39 -0
- package/dist/clis/instagram/save.d.ts +1 -0
- package/dist/clis/instagram/save.js +45 -0
- package/dist/clis/instagram/saved.d.ts +1 -0
- package/dist/clis/instagram/saved.js +38 -0
- package/dist/clis/instagram/search.d.ts +1 -0
- package/dist/clis/instagram/search.js +38 -0
- package/dist/clis/instagram/unfollow.d.ts +1 -0
- package/dist/clis/instagram/unfollow.js +40 -0
- package/dist/clis/instagram/unlike.d.ts +1 -0
- package/dist/clis/instagram/unlike.js +45 -0
- package/dist/clis/instagram/unsave.d.ts +1 -0
- package/dist/clis/instagram/unsave.js +45 -0
- package/dist/clis/instagram/user.d.ts +1 -0
- package/dist/clis/instagram/user.js +48 -0
- package/dist/clis/jd/add-cart.d.ts +1 -0
- package/dist/clis/jd/add-cart.js +71 -0
- package/dist/clis/jd/cart.d.ts +1 -0
- package/dist/clis/jd/cart.js +79 -0
- package/dist/clis/jd/commands.test.d.ts +5 -0
- package/dist/clis/jd/commands.test.js +64 -0
- package/dist/clis/jd/detail.d.ts +1 -0
- package/dist/clis/jd/detail.js +62 -0
- package/dist/clis/jd/reviews.d.ts +1 -0
- package/dist/clis/jd/reviews.js +54 -0
- package/dist/clis/jd/search.d.ts +1 -0
- package/dist/clis/jd/search.js +65 -0
- package/dist/clis/jianyu/search.d.ts +14 -0
- package/dist/clis/jianyu/search.js +135 -0
- package/dist/clis/jianyu/search.test.d.ts +1 -0
- package/dist/clis/jianyu/search.test.js +23 -0
- package/dist/clis/jike/post.d.ts +1 -0
- package/dist/clis/jike/post.js +61 -0
- package/dist/clis/jike/topic.d.ts +1 -0
- package/dist/clis/jike/topic.js +51 -0
- package/dist/clis/jike/user.d.ts +1 -0
- package/dist/clis/jike/user.js +50 -0
- package/dist/clis/jimeng/generate.d.ts +1 -0
- package/dist/clis/jimeng/generate.js +83 -0
- package/dist/clis/jimeng/history.d.ts +1 -0
- package/dist/clis/jimeng/history.js +47 -0
- package/dist/clis/jimeng/new.d.ts +1 -0
- package/dist/clis/jimeng/new.js +43 -0
- package/dist/clis/jimeng/workspaces.d.ts +1 -0
- package/dist/clis/jimeng/workspaces.js +41 -0
- package/dist/clis/linux-do/categories.d.ts +1 -0
- package/dist/clis/linux-do/categories.js +65 -0
- package/dist/clis/linux-do/search.d.ts +1 -0
- package/dist/clis/linux-do/search.js +41 -0
- package/dist/clis/linux-do/tags.d.ts +1 -0
- package/dist/clis/linux-do/tags.js +39 -0
- package/dist/clis/linux-do/topic-content.test.js +5 -5
- package/dist/clis/linux-do/topic.d.ts +1 -0
- package/dist/clis/linux-do/topic.js +56 -0
- package/dist/clis/linux-do/user-posts.d.ts +1 -0
- package/dist/clis/linux-do/user-posts.js +61 -0
- package/dist/clis/linux-do/user-topics.d.ts +1 -0
- package/dist/clis/linux-do/user-topics.js +48 -0
- package/dist/clis/lobsters/active.d.ts +1 -0
- package/dist/clis/lobsters/active.js +26 -0
- package/dist/clis/lobsters/hot.d.ts +1 -0
- package/dist/clis/lobsters/hot.js +26 -0
- package/dist/clis/lobsters/newest.d.ts +1 -0
- package/dist/clis/lobsters/newest.js +26 -0
- package/dist/clis/lobsters/tag.d.ts +1 -0
- package/dist/clis/lobsters/tag.js +32 -0
- package/dist/clis/pixiv/detail.d.ts +1 -0
- package/dist/clis/pixiv/detail.js +58 -0
- package/dist/clis/pixiv/ranking.d.ts +1 -0
- package/dist/clis/pixiv/ranking.js +59 -0
- package/dist/clis/pixiv/user.d.ts +1 -0
- package/dist/clis/pixiv/user.js +52 -0
- package/dist/clis/quark/ls.d.ts +1 -0
- package/dist/clis/quark/ls.js +63 -0
- package/dist/clis/quark/mkdir.d.ts +1 -0
- package/dist/clis/quark/mkdir.js +36 -0
- package/dist/clis/quark/mv.d.ts +1 -0
- package/dist/clis/quark/mv.js +53 -0
- package/dist/clis/quark/rename.d.ts +1 -0
- package/dist/clis/quark/rename.js +26 -0
- package/dist/clis/quark/rm.d.ts +1 -0
- package/dist/clis/quark/rm.js +24 -0
- package/dist/clis/quark/save.d.ts +1 -0
- package/dist/clis/quark/save.js +80 -0
- package/dist/clis/quark/share-tree.d.ts +1 -0
- package/dist/clis/quark/share-tree.js +45 -0
- package/dist/clis/quark/utils.d.ts +50 -0
- package/dist/clis/quark/utils.js +146 -0
- package/dist/clis/quark/utils.test.d.ts +1 -0
- package/dist/clis/quark/utils.test.js +58 -0
- package/dist/clis/reddit/frontpage.d.ts +1 -0
- package/dist/clis/reddit/frontpage.js +31 -0
- package/dist/clis/reddit/hot.d.ts +1 -0
- package/dist/clis/reddit/hot.js +45 -0
- package/dist/clis/reddit/popular.d.ts +1 -0
- package/dist/clis/reddit/popular.js +41 -0
- package/dist/clis/reddit/search.d.ts +1 -0
- package/dist/clis/reddit/search.js +65 -0
- package/dist/clis/reddit/subreddit.d.ts +1 -0
- package/dist/clis/reddit/subreddit.js +52 -0
- package/dist/clis/reddit/user-comments.d.ts +1 -0
- package/dist/clis/reddit/user-comments.js +44 -0
- package/dist/clis/reddit/user-posts.d.ts +1 -0
- package/dist/clis/reddit/user-posts.js +42 -0
- package/dist/clis/reddit/user.d.ts +1 -0
- package/dist/clis/reddit/user.js +37 -0
- package/dist/clis/stackoverflow/bounties.d.ts +1 -0
- package/dist/clis/stackoverflow/bounties.js +27 -0
- package/dist/clis/stackoverflow/hot.d.ts +1 -0
- package/dist/clis/stackoverflow/hot.js +24 -0
- package/dist/clis/stackoverflow/search.d.ts +1 -0
- package/dist/clis/stackoverflow/search.js +27 -0
- package/dist/clis/stackoverflow/unanswered.d.ts +1 -0
- package/dist/clis/stackoverflow/unanswered.js +26 -0
- package/dist/clis/steam/top-sellers.d.ts +1 -0
- package/dist/clis/steam/top-sellers.js +25 -0
- package/dist/clis/taobao/add-cart.d.ts +1 -0
- package/dist/clis/taobao/add-cart.js +149 -0
- package/dist/clis/taobao/cart.d.ts +1 -0
- package/dist/clis/taobao/cart.js +95 -0
- package/dist/clis/taobao/commands.test.d.ts +5 -0
- package/dist/clis/taobao/commands.test.js +64 -0
- package/dist/clis/taobao/detail.d.ts +1 -0
- package/dist/clis/taobao/detail.js +70 -0
- package/dist/clis/taobao/reviews.d.ts +1 -0
- package/dist/clis/taobao/reviews.js +76 -0
- package/dist/clis/taobao/search.d.ts +1 -0
- package/dist/clis/taobao/search.js +96 -0
- package/dist/clis/tiktok/comment.d.ts +1 -0
- package/dist/clis/tiktok/comment.js +57 -0
- package/dist/clis/tiktok/explore.d.ts +1 -0
- package/dist/clis/tiktok/explore.js +35 -0
- package/dist/clis/tiktok/follow.d.ts +1 -0
- package/dist/clis/tiktok/follow.js +39 -0
- package/dist/clis/tiktok/following.d.ts +1 -0
- package/dist/clis/tiktok/following.js +42 -0
- package/dist/clis/tiktok/friends.d.ts +1 -0
- package/dist/clis/tiktok/friends.js +43 -0
- package/dist/clis/tiktok/like.d.ts +1 -0
- package/dist/clis/tiktok/like.js +33 -0
- package/dist/clis/tiktok/live.d.ts +1 -0
- package/dist/clis/tiktok/live.js +47 -0
- package/dist/clis/tiktok/notifications.d.ts +1 -0
- package/dist/clis/tiktok/notifications.js +49 -0
- package/dist/clis/tiktok/profile.d.ts +1 -0
- package/dist/clis/tiktok/profile.js +54 -0
- package/dist/clis/tiktok/save.d.ts +1 -0
- package/dist/clis/tiktok/save.js +29 -0
- package/dist/clis/tiktok/search.d.ts +1 -0
- package/dist/clis/tiktok/search.js +39 -0
- package/dist/clis/tiktok/unfollow.d.ts +1 -0
- package/dist/clis/tiktok/unfollow.js +44 -0
- package/dist/clis/tiktok/unlike.d.ts +1 -0
- package/dist/clis/tiktok/unlike.js +33 -0
- package/dist/clis/tiktok/unsave.d.ts +1 -0
- package/dist/clis/tiktok/unsave.js +31 -0
- package/dist/clis/tiktok/user.d.ts +1 -0
- package/dist/clis/tiktok/user.js +41 -0
- package/dist/clis/twitter/reply.js +3 -8
- package/dist/clis/twitter/reply.test.js +5 -5
- package/dist/clis/v2ex/hot.d.ts +1 -0
- package/dist/clis/v2ex/hot.js +25 -0
- package/dist/clis/v2ex/latest.d.ts +1 -0
- package/dist/clis/v2ex/latest.js +25 -0
- package/dist/clis/v2ex/member.d.ts +1 -0
- package/dist/clis/v2ex/member.js +27 -0
- package/dist/clis/v2ex/node.d.ts +1 -0
- package/dist/clis/v2ex/node.js +38 -0
- package/dist/clis/v2ex/nodes.d.ts +1 -0
- package/dist/clis/v2ex/nodes.js +25 -0
- package/dist/clis/v2ex/replies.d.ts +1 -0
- package/dist/clis/v2ex/replies.js +26 -0
- package/dist/clis/v2ex/topic.d.ts +1 -0
- package/dist/clis/v2ex/topic.js +30 -0
- package/dist/clis/v2ex/user.d.ts +1 -0
- package/dist/clis/v2ex/user.js +33 -0
- package/dist/clis/xiaoe/catalog.d.ts +1 -0
- package/dist/clis/xiaoe/catalog.js +125 -0
- package/dist/clis/xiaoe/content.d.ts +1 -0
- package/dist/clis/xiaoe/content.js +39 -0
- package/dist/clis/xiaoe/courses.d.ts +1 -0
- package/dist/clis/xiaoe/courses.js +69 -0
- package/dist/clis/xiaoe/detail.d.ts +1 -0
- package/dist/clis/xiaoe/detail.js +35 -0
- package/dist/clis/xiaoe/play-url.d.ts +1 -0
- package/dist/clis/xiaoe/play-url.js +120 -0
- package/dist/clis/xiaohongshu/feed.d.ts +1 -0
- package/dist/clis/xiaohongshu/feed.js +32 -0
- package/dist/clis/xiaohongshu/note.js +8 -3
- package/dist/clis/xiaohongshu/note.test.js +11 -0
- package/dist/clis/xiaohongshu/notifications.d.ts +1 -0
- package/dist/clis/xiaohongshu/notifications.js +38 -0
- package/dist/clis/xueqiu/earnings-date.d.ts +1 -0
- package/dist/clis/xueqiu/earnings-date.js +61 -0
- package/dist/clis/xueqiu/feed.d.ts +1 -0
- package/dist/clis/xueqiu/feed.js +48 -0
- package/dist/clis/xueqiu/groups.d.ts +1 -0
- package/dist/clis/xueqiu/groups.js +25 -0
- package/dist/clis/xueqiu/hot-stock.d.ts +1 -0
- package/dist/clis/xueqiu/hot-stock.js +44 -0
- package/dist/clis/xueqiu/hot.d.ts +1 -0
- package/dist/clis/xueqiu/hot.js +44 -0
- package/dist/clis/xueqiu/kline.d.ts +1 -0
- package/dist/clis/xueqiu/kline.js +64 -0
- package/dist/clis/xueqiu/search.d.ts +1 -0
- package/dist/clis/xueqiu/search.js +49 -0
- package/dist/clis/xueqiu/stock.d.ts +1 -0
- package/dist/clis/xueqiu/stock.js +72 -0
- package/dist/clis/xueqiu/watchlist.d.ts +1 -0
- package/dist/clis/xueqiu/watchlist.js +45 -0
- package/dist/clis/zhihu/answer.d.ts +1 -0
- package/dist/clis/zhihu/answer.js +194 -0
- package/dist/clis/zhihu/answer.test.d.ts +1 -0
- package/dist/clis/zhihu/answer.test.js +81 -0
- package/dist/clis/zhihu/comment.d.ts +1 -0
- package/dist/clis/zhihu/comment.js +335 -0
- package/dist/clis/zhihu/comment.test.d.ts +1 -0
- package/dist/clis/zhihu/comment.test.js +54 -0
- package/dist/clis/zhihu/favorite.d.ts +1 -0
- package/dist/clis/zhihu/favorite.js +224 -0
- package/dist/clis/zhihu/favorite.test.d.ts +1 -0
- package/dist/clis/zhihu/favorite.test.js +196 -0
- package/dist/clis/zhihu/follow.d.ts +1 -0
- package/dist/clis/zhihu/follow.js +80 -0
- package/dist/clis/zhihu/follow.test.d.ts +1 -0
- package/dist/clis/zhihu/follow.test.js +45 -0
- package/dist/clis/zhihu/hot.d.ts +1 -0
- package/dist/clis/zhihu/hot.js +43 -0
- package/dist/clis/zhihu/like.d.ts +1 -0
- package/dist/clis/zhihu/like.js +91 -0
- package/dist/clis/zhihu/like.test.d.ts +1 -0
- package/dist/clis/zhihu/like.test.js +64 -0
- package/dist/clis/zhihu/search.d.ts +1 -0
- package/dist/clis/zhihu/search.js +52 -0
- package/dist/clis/zhihu/target.d.ts +24 -0
- package/dist/clis/zhihu/target.js +91 -0
- package/dist/clis/zhihu/target.test.d.ts +1 -0
- package/dist/clis/zhihu/target.test.js +77 -0
- package/dist/clis/zhihu/write-shared.d.ts +32 -0
- package/dist/clis/zhihu/write-shared.js +221 -0
- package/dist/clis/zhihu/write-shared.test.d.ts +1 -0
- package/dist/clis/zhihu/write-shared.test.js +175 -0
- package/dist/src/browser/bridge.d.ts +2 -0
- package/dist/src/browser/bridge.js +30 -24
- package/dist/src/browser/daemon-client.d.ts +30 -10
- package/dist/src/browser/daemon-client.js +42 -27
- package/dist/src/browser/daemon-client.test.js +32 -25
- package/dist/src/browser/dom-helpers.test.js +3 -2
- package/dist/src/browser/errors.d.ts +26 -1
- package/dist/src/browser/errors.js +40 -7
- package/dist/src/browser/errors.test.d.ts +1 -0
- package/dist/src/browser/errors.test.js +51 -0
- package/dist/src/browser/index.d.ts +2 -1
- package/dist/src/browser/index.js +1 -1
- package/dist/src/browser/page.d.ts +9 -8
- package/dist/src/browser/page.js +33 -31
- package/dist/src/browser.test.js +27 -8
- package/dist/src/build-manifest.d.ts +5 -11
- package/dist/src/build-manifest.js +6 -75
- package/dist/src/build-manifest.test.js +1 -39
- package/dist/src/cascade.js +3 -2
- package/dist/src/cli.d.ts +3 -3
- package/dist/src/cli.js +73 -65
- package/dist/src/cli.test.js +20 -15
- package/dist/src/clis/binance/asks.d.ts +1 -0
- package/dist/src/clis/binance/asks.js +20 -0
- package/dist/src/clis/binance/commands.test.d.ts +3 -0
- package/dist/src/clis/binance/commands.test.js +58 -0
- package/dist/src/clis/binance/depth.d.ts +1 -0
- package/dist/src/clis/binance/depth.js +20 -0
- package/dist/src/clis/binance/gainers.d.ts +1 -0
- package/dist/src/clis/binance/gainers.js +21 -0
- package/dist/src/clis/binance/klines.d.ts +1 -0
- package/dist/src/clis/binance/klines.js +20 -0
- package/dist/src/clis/binance/losers.d.ts +1 -0
- package/dist/src/clis/binance/losers.js +21 -0
- package/dist/src/clis/binance/pairs.d.ts +1 -0
- package/dist/src/clis/binance/pairs.js +20 -0
- package/dist/src/clis/binance/price.d.ts +1 -0
- package/dist/src/clis/binance/price.js +17 -0
- package/dist/src/clis/binance/prices.d.ts +1 -0
- package/dist/src/clis/binance/prices.js +18 -0
- package/dist/src/clis/binance/ticker.d.ts +1 -0
- package/dist/src/clis/binance/ticker.js +20 -0
- package/dist/src/clis/binance/top.d.ts +1 -0
- package/dist/src/clis/binance/top.js +20 -0
- package/dist/src/clis/binance/trades.d.ts +1 -0
- package/dist/src/clis/binance/trades.js +19 -0
- package/dist/src/commanderAdapter.js +19 -6
- package/dist/src/completion-fast.d.ts +25 -0
- package/dist/src/completion-fast.js +140 -0
- package/dist/src/completion.d.ts +1 -0
- package/dist/src/completion.js +1 -0
- package/dist/src/diagnostic.d.ts +1 -0
- package/dist/src/diagnostic.js +64 -2
- package/dist/src/diagnostic.test.js +93 -3
- package/dist/src/discovery.d.ts +3 -3
- package/dist/src/discovery.js +34 -97
- package/dist/src/doctor.d.ts +2 -0
- package/dist/src/doctor.js +59 -31
- package/dist/src/doctor.test.js +89 -16
- package/dist/src/download/index.d.ts +1 -1
- package/dist/src/engine.test.js +4 -19
- package/dist/src/execution.js +1 -13
- package/dist/src/explore.js +1 -1
- package/dist/src/generate-verified.d.ts +105 -0
- package/dist/src/generate-verified.js +696 -0
- package/dist/src/generate-verified.test.d.ts +1 -0
- package/dist/src/generate-verified.test.js +925 -0
- package/dist/src/generate.d.ts +11 -6
- package/dist/src/generate.js +4 -7
- package/dist/src/main.js +65 -12
- package/dist/src/pipeline/steps/download.d.ts +1 -17
- package/dist/src/pipeline/steps/download.js +20 -31
- package/dist/src/pipeline/steps/intercept.d.ts +1 -1
- package/dist/src/pipeline/steps/intercept.js +1 -1
- package/dist/src/pipeline/steps/tap.d.ts +1 -1
- package/dist/src/pipeline/steps/tap.js +1 -1
- package/dist/src/plugin-scaffold.d.ts +2 -2
- package/dist/src/plugin-scaffold.js +24 -21
- package/dist/src/plugin-scaffold.test.js +1 -1
- package/dist/src/plugin.d.ts +3 -2
- package/dist/src/plugin.js +29 -14
- package/dist/src/plugin.test.js +47 -32
- package/dist/src/record.js +26 -25
- package/dist/src/runtime-detect.js +3 -7
- package/dist/src/scripts/framework.d.ts +3 -0
- package/dist/src/scripts/framework.js +8 -4
- package/dist/src/scripts/store.d.ts +5 -1
- package/dist/src/scripts/store.js +5 -1
- package/dist/src/skill-generate.d.ts +30 -0
- package/dist/src/skill-generate.js +75 -0
- package/dist/src/skill-generate.test.d.ts +1 -0
- package/dist/src/skill-generate.test.js +173 -0
- package/dist/src/synthesize.d.ts +1 -1
- package/dist/src/synthesize.js +7 -8
- package/dist/src/types.d.ts +3 -1
- package/package.json +5 -5
- package/dist/clis/bilibili/hot.yaml +0 -38
- package/dist/clis/bluesky/feeds.yaml +0 -29
- package/dist/clis/bluesky/followers.yaml +0 -33
- package/dist/clis/bluesky/following.yaml +0 -33
- package/dist/clis/bluesky/profile.yaml +0 -27
- package/dist/clis/bluesky/search.yaml +0 -34
- package/dist/clis/bluesky/starter-packs.yaml +0 -34
- package/dist/clis/bluesky/thread.yaml +0 -32
- package/dist/clis/bluesky/trending.yaml +0 -27
- package/dist/clis/bluesky/user.yaml +0 -34
- package/dist/clis/devto/tag.yaml +0 -34
- package/dist/clis/devto/top.yaml +0 -29
- package/dist/clis/devto/user.yaml +0 -33
- package/dist/clis/dictionary/examples.yaml +0 -25
- package/dist/clis/dictionary/search.yaml +0 -27
- package/dist/clis/dictionary/synonyms.yaml +0 -25
- package/dist/clis/douban/subject.yaml +0 -107
- package/dist/clis/douban/top250.yaml +0 -70
- package/dist/clis/facebook/add-friend.yaml +0 -43
- package/dist/clis/facebook/events.yaml +0 -44
- package/dist/clis/facebook/feed.yaml +0 -63
- package/dist/clis/facebook/friends.yaml +0 -42
- package/dist/clis/facebook/groups.yaml +0 -50
- package/dist/clis/facebook/join-group.yaml +0 -44
- package/dist/clis/facebook/memories.yaml +0 -39
- package/dist/clis/facebook/notifications.yaml +0 -40
- package/dist/clis/facebook/profile.yaml +0 -37
- package/dist/clis/facebook/search.yaml +0 -47
- package/dist/clis/hackernews/ask.yaml +0 -38
- package/dist/clis/hackernews/best.yaml +0 -38
- package/dist/clis/hackernews/jobs.yaml +0 -36
- package/dist/clis/hackernews/new.yaml +0 -38
- package/dist/clis/hackernews/search.yaml +0 -44
- package/dist/clis/hackernews/show.yaml +0 -38
- package/dist/clis/hackernews/top.yaml +0 -38
- package/dist/clis/hackernews/user.yaml +0 -25
- package/dist/clis/hupu/hot.yaml +0 -43
- package/dist/clis/instagram/comment.yaml +0 -52
- package/dist/clis/instagram/explore.yaml +0 -43
- package/dist/clis/instagram/follow.yaml +0 -41
- package/dist/clis/instagram/followers.yaml +0 -51
- package/dist/clis/instagram/following.yaml +0 -51
- package/dist/clis/instagram/like.yaml +0 -46
- package/dist/clis/instagram/profile.yaml +0 -42
- package/dist/clis/instagram/save.yaml +0 -46
- package/dist/clis/instagram/saved.yaml +0 -40
- package/dist/clis/instagram/search.yaml +0 -44
- package/dist/clis/instagram/unfollow.yaml +0 -38
- package/dist/clis/instagram/unlike.yaml +0 -46
- package/dist/clis/instagram/unsave.yaml +0 -46
- package/dist/clis/instagram/user.yaml +0 -54
- package/dist/clis/jike/post.yaml +0 -59
- package/dist/clis/jike/topic.yaml +0 -53
- package/dist/clis/jike/user.yaml +0 -52
- package/dist/clis/jimeng/generate.yaml +0 -85
- package/dist/clis/jimeng/history.yaml +0 -46
- package/dist/clis/linux-do/categories.yaml +0 -70
- package/dist/clis/linux-do/search.yaml +0 -48
- package/dist/clis/linux-do/tags.yaml +0 -41
- package/dist/clis/linux-do/topic.yaml +0 -62
- package/dist/clis/linux-do/user-posts.yaml +0 -67
- package/dist/clis/linux-do/user-topics.yaml +0 -54
- package/dist/clis/lobsters/active.yaml +0 -29
- package/dist/clis/lobsters/hot.yaml +0 -29
- package/dist/clis/lobsters/newest.yaml +0 -29
- package/dist/clis/lobsters/tag.yaml +0 -34
- package/dist/clis/pixiv/detail.yaml +0 -49
- package/dist/clis/pixiv/ranking.yaml +0 -53
- package/dist/clis/pixiv/user.yaml +0 -46
- package/dist/clis/reddit/frontpage.yaml +0 -30
- package/dist/clis/reddit/hot.yaml +0 -47
- package/dist/clis/reddit/popular.yaml +0 -40
- package/dist/clis/reddit/search.yaml +0 -61
- package/dist/clis/reddit/subreddit.yaml +0 -50
- package/dist/clis/reddit/user-comments.yaml +0 -46
- package/dist/clis/reddit/user-posts.yaml +0 -44
- package/dist/clis/reddit/user.yaml +0 -40
- package/dist/clis/stackoverflow/bounties.yaml +0 -29
- package/dist/clis/stackoverflow/hot.yaml +0 -28
- package/dist/clis/stackoverflow/search.yaml +0 -33
- package/dist/clis/stackoverflow/unanswered.yaml +0 -28
- package/dist/clis/steam/top-sellers.yaml +0 -29
- package/dist/clis/tiktok/comment.yaml +0 -66
- package/dist/clis/tiktok/explore.yaml +0 -39
- package/dist/clis/tiktok/follow.yaml +0 -39
- package/dist/clis/tiktok/following.yaml +0 -46
- package/dist/clis/tiktok/friends.yaml +0 -47
- package/dist/clis/tiktok/like.yaml +0 -38
- package/dist/clis/tiktok/live.yaml +0 -51
- package/dist/clis/tiktok/notifications.yaml +0 -52
- package/dist/clis/tiktok/profile.yaml +0 -45
- package/dist/clis/tiktok/save.yaml +0 -34
- package/dist/clis/tiktok/search.yaml +0 -47
- package/dist/clis/tiktok/unfollow.yaml +0 -44
- package/dist/clis/tiktok/unlike.yaml +0 -38
- package/dist/clis/tiktok/unsave.yaml +0 -36
- package/dist/clis/tiktok/user.yaml +0 -44
- package/dist/clis/v2ex/hot.yaml +0 -28
- package/dist/clis/v2ex/latest.yaml +0 -28
- package/dist/clis/v2ex/member.yaml +0 -29
- package/dist/clis/v2ex/node.yaml +0 -34
- package/dist/clis/v2ex/nodes.yaml +0 -31
- package/dist/clis/v2ex/replies.yaml +0 -32
- package/dist/clis/v2ex/topic.yaml +0 -33
- package/dist/clis/v2ex/user.yaml +0 -34
- package/dist/clis/xiaoe/catalog.yaml +0 -129
- package/dist/clis/xiaoe/content.yaml +0 -43
- package/dist/clis/xiaoe/courses.yaml +0 -73
- package/dist/clis/xiaoe/detail.yaml +0 -39
- package/dist/clis/xiaoe/play-url.yaml +0 -124
- package/dist/clis/xiaohongshu/feed.yaml +0 -31
- package/dist/clis/xiaohongshu/notifications.yaml +0 -37
- package/dist/clis/xueqiu/earnings-date.yaml +0 -69
- package/dist/clis/xueqiu/feed.yaml +0 -53
- package/dist/clis/xueqiu/groups.yaml +0 -23
- package/dist/clis/xueqiu/hot-stock.yaml +0 -49
- package/dist/clis/xueqiu/hot.yaml +0 -46
- package/dist/clis/xueqiu/kline.yaml +0 -65
- package/dist/clis/xueqiu/search.yaml +0 -55
- package/dist/clis/xueqiu/stock.yaml +0 -69
- package/dist/clis/xueqiu/watchlist.yaml +0 -46
- package/dist/clis/zhihu/hot.yaml +0 -46
- package/dist/clis/zhihu/search.yaml +0 -59
- package/dist/src/browser/discover.d.ts +0 -15
- package/dist/src/browser/discover.js +0 -19
- package/dist/src/yaml-schema.d.ts +0 -29
- package/dist/src/yaml-schema.js +0 -22
package/dist/src/cli.js
CHANGED
|
@@ -21,11 +21,11 @@ import { registerAllCommands } from './commanderAdapter.js';
|
|
|
21
21
|
import { EXIT_CODES, getErrorMessage } from './errors.js';
|
|
22
22
|
import { daemonStatus, daemonStop, daemonRestart } from './commands/daemon.js';
|
|
23
23
|
const CLI_FILE = fileURLToPath(import.meta.url);
|
|
24
|
-
/** Create a browser page for
|
|
25
|
-
async function
|
|
24
|
+
/** Create a browser page for browser commands. Uses a dedicated browser workspace for session persistence. */
|
|
25
|
+
async function getBrowserPage() {
|
|
26
26
|
const { BrowserBridge } = await import('./browser/index.js');
|
|
27
27
|
const bridge = new BrowserBridge();
|
|
28
|
-
return bridge.connect({ timeout: 30, workspace: '
|
|
28
|
+
return bridge.connect({ timeout: 30, workspace: 'browser:default' });
|
|
29
29
|
}
|
|
30
30
|
function applyVerbose(opts) {
|
|
31
31
|
if (opts.verbose)
|
|
@@ -171,24 +171,30 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
171
171
|
});
|
|
172
172
|
program
|
|
173
173
|
.command('generate')
|
|
174
|
-
.description('One-shot: explore → synthesize → register')
|
|
174
|
+
.description('One-shot: explore → synthesize → verify → register')
|
|
175
175
|
.argument('<url>')
|
|
176
176
|
.option('--goal <text>')
|
|
177
177
|
.option('--site <name>')
|
|
178
|
+
.option('--format <fmt>', 'Output format: table, json', 'table')
|
|
179
|
+
.option('--no-register', 'Verify the generated adapter without registering it')
|
|
178
180
|
.option('-v, --verbose', 'Debug output')
|
|
179
181
|
.action(async (url, opts) => {
|
|
180
182
|
applyVerbose(opts);
|
|
181
|
-
const {
|
|
183
|
+
const { generateVerifiedFromUrl, renderGenerateVerifiedSummary } = await import('./generate-verified.js');
|
|
182
184
|
const workspace = `generate:${inferHost(url, opts.site)}`;
|
|
183
|
-
const r = await
|
|
185
|
+
const r = await generateVerifiedFromUrl({
|
|
184
186
|
url,
|
|
185
187
|
BrowserFactory: getBrowserFactory(),
|
|
186
188
|
goal: opts.goal,
|
|
187
189
|
site: opts.site,
|
|
188
190
|
workspace,
|
|
191
|
+
noRegister: opts.register === false,
|
|
189
192
|
});
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
if (opts.format === 'json')
|
|
194
|
+
console.log(JSON.stringify(r, null, 2));
|
|
195
|
+
else
|
|
196
|
+
console.log(renderGenerateVerifiedSummary(r));
|
|
197
|
+
process.exitCode = r.status === 'success' ? EXIT_CODES.SUCCESS : EXIT_CODES.GENERIC_ERROR;
|
|
192
198
|
});
|
|
193
199
|
// ── Built-in: record ─────────────────────────────────────────────────────
|
|
194
200
|
program
|
|
@@ -235,22 +241,22 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
235
241
|
}, { workspace });
|
|
236
242
|
console.log(renderCascadeResult(result));
|
|
237
243
|
});
|
|
238
|
-
// ── Built-in:
|
|
244
|
+
// ── Built-in: browser (browser control for Claude Code skill) ───────────────
|
|
239
245
|
//
|
|
240
246
|
// Make websites accessible for AI agents.
|
|
241
|
-
// All commands wrapped in
|
|
242
|
-
const
|
|
243
|
-
.command('
|
|
247
|
+
// All commands wrapped in browserAction() for consistent error handling.
|
|
248
|
+
const browser = program
|
|
249
|
+
.command('browser')
|
|
244
250
|
.description('Browser control — navigate, click, type, extract, wait (no LLM needed)');
|
|
245
|
-
/** Wrap
|
|
246
|
-
function
|
|
251
|
+
/** Wrap browser actions with error handling and optional --json output */
|
|
252
|
+
function browserAction(fn) {
|
|
247
253
|
return async (...args) => {
|
|
248
254
|
try {
|
|
249
|
-
const page = await
|
|
255
|
+
const page = await getBrowserPage();
|
|
250
256
|
await fn(page, ...args);
|
|
251
257
|
}
|
|
252
258
|
catch (err) {
|
|
253
|
-
const msg =
|
|
259
|
+
const msg = getErrorMessage(err);
|
|
254
260
|
if (msg.includes('Extension not connected') || msg.includes('Daemon')) {
|
|
255
261
|
console.error(`Browser not connected. Run 'opencli doctor' to diagnose.`);
|
|
256
262
|
}
|
|
@@ -267,14 +273,14 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
267
273
|
// ── Navigation ──
|
|
268
274
|
/** Network interceptor JS — injected on every open/navigate to capture fetch/XHR */
|
|
269
275
|
const NETWORK_INTERCEPTOR_JS = `(function(){if(window.__opencli_net)return;window.__opencli_net=[];var M=200,B=50000,F=window.fetch;window.fetch=async function(){var r=await F.apply(this,arguments);try{var ct=r.headers.get('content-type')||'';if(ct.includes('json')||ct.includes('text')){var c=r.clone(),t=await c.text();if(window.__opencli_net.length<M){var b=null;if(t.length<=B)try{b=JSON.parse(t)}catch(e){b=t}window.__opencli_net.push({url:r.url||(arguments[0]&&arguments[0].url)||String(arguments[0]),method:(arguments[1]&&arguments[1].method)||'GET',status:r.status,size:t.length,ct:ct,body:b})}}}catch(e){}return r};var X=XMLHttpRequest.prototype,O=X.open,S=X.send;X.open=function(m,u){this._om=m;this._ou=u;return O.apply(this,arguments)};X.send=function(){var x=this;x.addEventListener('load',function(){try{var ct=x.getResponseHeader('content-type')||'';if((ct.includes('json')||ct.includes('text'))&&window.__opencli_net.length<M){var t=x.responseText,b=null;if(t&&t.length<=B)try{b=JSON.parse(t)}catch(e){b=t}window.__opencli_net.push({url:x._ou,method:x._om||'GET',status:x.status,size:t?t.length:0,ct:ct,body:b})}}catch(e){}});return S.apply(this,arguments)}})()`;
|
|
270
|
-
|
|
271
|
-
.action(
|
|
276
|
+
browser.command('open').argument('<url>').description('Open URL in automation window')
|
|
277
|
+
.action(browserAction(async (page, url) => {
|
|
272
278
|
// Start session-level capture before navigation (catches initial requests)
|
|
273
|
-
await page.startNetworkCapture?.();
|
|
279
|
+
const hasSessionCapture = await page.startNetworkCapture?.().then(() => true).catch(() => false);
|
|
274
280
|
await page.goto(url);
|
|
275
281
|
await page.wait(2);
|
|
276
|
-
// Fallback:
|
|
277
|
-
if (!
|
|
282
|
+
// Fallback: inject JS interceptor when session capture is unavailable
|
|
283
|
+
if (!hasSessionCapture) {
|
|
278
284
|
try {
|
|
279
285
|
await page.evaluate(NETWORK_INTERCEPTOR_JS);
|
|
280
286
|
}
|
|
@@ -282,15 +288,15 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
282
288
|
}
|
|
283
289
|
console.log(`Navigated to: ${await page.getCurrentUrl?.() ?? url}`);
|
|
284
290
|
}));
|
|
285
|
-
|
|
286
|
-
.action(
|
|
291
|
+
browser.command('back').description('Go back in browser history')
|
|
292
|
+
.action(browserAction(async (page) => {
|
|
287
293
|
await page.evaluate('history.back()');
|
|
288
294
|
await page.wait(2);
|
|
289
295
|
console.log('Navigated back');
|
|
290
296
|
}));
|
|
291
|
-
|
|
297
|
+
browser.command('scroll').argument('<direction>', 'up or down').option('--amount <pixels>', 'Pixels to scroll', '500')
|
|
292
298
|
.description('Scroll page')
|
|
293
|
-
.action(
|
|
299
|
+
.action(browserAction(async (page, direction, opts) => {
|
|
294
300
|
if (direction !== 'up' && direction !== 'down') {
|
|
295
301
|
console.error(`Invalid direction "${direction}". Use "up" or "down".`);
|
|
296
302
|
process.exitCode = EXIT_CODES.USAGE_ERROR;
|
|
@@ -300,16 +306,16 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
300
306
|
console.log(`Scrolled ${direction}`);
|
|
301
307
|
}));
|
|
302
308
|
// ── Inspect ──
|
|
303
|
-
|
|
304
|
-
.action(
|
|
309
|
+
browser.command('state').description('Page state: URL, title, interactive elements with [N] indices')
|
|
310
|
+
.action(browserAction(async (page) => {
|
|
305
311
|
const snapshot = await page.snapshot({ viewportExpand: 2000 });
|
|
306
312
|
const url = await page.getCurrentUrl?.() ?? '';
|
|
307
313
|
console.log(`URL: ${url}\n`);
|
|
308
314
|
console.log(typeof snapshot === 'string' ? snapshot : JSON.stringify(snapshot, null, 2));
|
|
309
315
|
}));
|
|
310
|
-
|
|
316
|
+
browser.command('screenshot').argument('[path]', 'Save to file (base64 if omitted)')
|
|
311
317
|
.description('Take screenshot')
|
|
312
|
-
.action(
|
|
318
|
+
.action(browserAction(async (page, path) => {
|
|
313
319
|
if (path) {
|
|
314
320
|
await page.screenshot({ path });
|
|
315
321
|
console.log(`Screenshot saved to: ${path}`);
|
|
@@ -319,45 +325,45 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
319
325
|
}
|
|
320
326
|
}));
|
|
321
327
|
// ── Get commands (structured data extraction) ──
|
|
322
|
-
const get =
|
|
328
|
+
const get = browser.command('get').description('Get page properties');
|
|
323
329
|
get.command('title').description('Page title')
|
|
324
|
-
.action(
|
|
330
|
+
.action(browserAction(async (page) => {
|
|
325
331
|
console.log(await page.evaluate('document.title'));
|
|
326
332
|
}));
|
|
327
333
|
get.command('url').description('Current page URL')
|
|
328
|
-
.action(
|
|
334
|
+
.action(browserAction(async (page) => {
|
|
329
335
|
console.log(await page.getCurrentUrl?.() ?? await page.evaluate('location.href'));
|
|
330
336
|
}));
|
|
331
337
|
get.command('text').argument('<index>', 'Element index').description('Element text content')
|
|
332
|
-
.action(
|
|
338
|
+
.action(browserAction(async (page, index) => {
|
|
333
339
|
const text = await page.evaluate(`document.querySelector('[data-opencli-ref="${index}"]')?.textContent?.trim()`);
|
|
334
340
|
console.log(text ?? '(empty)');
|
|
335
341
|
}));
|
|
336
342
|
get.command('value').argument('<index>', 'Element index').description('Input/textarea value')
|
|
337
|
-
.action(
|
|
343
|
+
.action(browserAction(async (page, index) => {
|
|
338
344
|
const val = await page.evaluate(`document.querySelector('[data-opencli-ref="${index}"]')?.value`);
|
|
339
345
|
console.log(val ?? '(empty)');
|
|
340
346
|
}));
|
|
341
347
|
get.command('html').option('--selector <css>', 'CSS selector scope').description('Page HTML (or scoped)')
|
|
342
|
-
.action(
|
|
348
|
+
.action(browserAction(async (page, opts) => {
|
|
343
349
|
const sel = opts.selector ? JSON.stringify(opts.selector) : 'null';
|
|
344
350
|
const html = await page.evaluate(`(${sel} ? document.querySelector(${sel})?.outerHTML : document.documentElement.outerHTML)?.slice(0, 50000)`);
|
|
345
351
|
console.log(html ?? '(empty)');
|
|
346
352
|
}));
|
|
347
353
|
get.command('attributes').argument('<index>', 'Element index').description('Element attributes')
|
|
348
|
-
.action(
|
|
354
|
+
.action(browserAction(async (page, index) => {
|
|
349
355
|
const attrs = await page.evaluate(`JSON.stringify(Object.fromEntries([...document.querySelector('[data-opencli-ref="${index}"]')?.attributes].map(a=>[a.name,a.value])))`);
|
|
350
356
|
console.log(attrs ?? '{}');
|
|
351
357
|
}));
|
|
352
358
|
// ── Interact ──
|
|
353
|
-
|
|
354
|
-
.action(
|
|
359
|
+
browser.command('click').argument('<index>', 'Element index from state').description('Click element by index')
|
|
360
|
+
.action(browserAction(async (page, index) => {
|
|
355
361
|
await page.click(index);
|
|
356
362
|
console.log(`Clicked element [${index}]`);
|
|
357
363
|
}));
|
|
358
|
-
|
|
364
|
+
browser.command('type').argument('<index>', 'Element index').argument('<text>', 'Text to type')
|
|
359
365
|
.description('Click element, then type text')
|
|
360
|
-
.action(
|
|
366
|
+
.action(browserAction(async (page, index, text) => {
|
|
361
367
|
await page.click(index);
|
|
362
368
|
await page.wait(0.3);
|
|
363
369
|
await page.typeText(index, text);
|
|
@@ -380,9 +386,9 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
380
386
|
console.log(`Typed "${text}" into element [${index}]`);
|
|
381
387
|
}
|
|
382
388
|
}));
|
|
383
|
-
|
|
389
|
+
browser.command('select').argument('<index>', 'Element index of <select>').argument('<option>', 'Option text')
|
|
384
390
|
.description('Select dropdown option')
|
|
385
|
-
.action(
|
|
391
|
+
.action(browserAction(async (page, index, option) => {
|
|
386
392
|
const result = await page.evaluate(`
|
|
387
393
|
(function() {
|
|
388
394
|
var sel = document.querySelector('[data-opencli-ref="${index}"]');
|
|
@@ -404,19 +410,19 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
404
410
|
console.log(`Selected "${result?.selected}" in element [${index}]`);
|
|
405
411
|
}
|
|
406
412
|
}));
|
|
407
|
-
|
|
413
|
+
browser.command('keys').argument('<key>', 'Key to press (Enter, Escape, Tab, Control+a)')
|
|
408
414
|
.description('Press keyboard key')
|
|
409
|
-
.action(
|
|
415
|
+
.action(browserAction(async (page, key) => {
|
|
410
416
|
await page.pressKey(key);
|
|
411
417
|
console.log(`Pressed: ${key}`);
|
|
412
418
|
}));
|
|
413
419
|
// ── Wait commands ──
|
|
414
|
-
|
|
420
|
+
browser.command('wait')
|
|
415
421
|
.argument('<type>', 'selector, text, or time')
|
|
416
422
|
.argument('[value]', 'CSS selector, text string, or seconds')
|
|
417
423
|
.option('--timeout <ms>', 'Timeout in milliseconds', '10000')
|
|
418
424
|
.description('Wait for selector, text, or time (e.g. wait selector ".loaded", wait text "Success", wait time 3)')
|
|
419
|
-
.action(
|
|
425
|
+
.action(browserAction(async (page, type, value, opts) => {
|
|
420
426
|
const timeout = parseInt(opts.timeout, 10);
|
|
421
427
|
if (type === 'time') {
|
|
422
428
|
const seconds = parseFloat(value ?? '2');
|
|
@@ -447,8 +453,8 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
447
453
|
}
|
|
448
454
|
}));
|
|
449
455
|
// ── Extract ──
|
|
450
|
-
|
|
451
|
-
.action(
|
|
456
|
+
browser.command('eval').argument('<js>', 'JavaScript code').description('Execute JS in page context, return result')
|
|
457
|
+
.action(browserAction(async (page, js) => {
|
|
452
458
|
const result = await page.evaluate(js);
|
|
453
459
|
if (typeof result === 'string')
|
|
454
460
|
console.log(result);
|
|
@@ -456,11 +462,11 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
456
462
|
console.log(JSON.stringify(result, null, 2));
|
|
457
463
|
}));
|
|
458
464
|
// ── Network (API discovery) ──
|
|
459
|
-
|
|
465
|
+
browser.command('network')
|
|
460
466
|
.option('--detail <index>', 'Show full response body of request at index')
|
|
461
467
|
.option('--all', 'Show all requests including static resources')
|
|
462
468
|
.description('Show captured network requests (auto-captured since last open)')
|
|
463
|
-
.action(
|
|
469
|
+
.action(browserAction(async (page, opts) => {
|
|
464
470
|
let items = [];
|
|
465
471
|
if (page.readNetworkCapture) {
|
|
466
472
|
const raw = await page.readNetworkCapture();
|
|
@@ -498,7 +504,7 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
498
504
|
items = JSON.parse(requests);
|
|
499
505
|
}
|
|
500
506
|
catch {
|
|
501
|
-
console.log('No network data captured. Run "
|
|
507
|
+
console.log('No network data captured. Run "browser open <url>" first.');
|
|
502
508
|
return;
|
|
503
509
|
}
|
|
504
510
|
}
|
|
@@ -537,7 +543,7 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
537
543
|
}
|
|
538
544
|
}));
|
|
539
545
|
// ── Init (adapter scaffolding) ──
|
|
540
|
-
|
|
546
|
+
browser.command('init')
|
|
541
547
|
.argument('<name>', 'Adapter name in site/command format (e.g. hn/top)')
|
|
542
548
|
.description('Generate adapter scaffold in ~/.opencli/clis/')
|
|
543
549
|
.action(async (name) => {
|
|
@@ -563,10 +569,10 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
563
569
|
console.log(`Adapter already exists: ${filePath}`);
|
|
564
570
|
return;
|
|
565
571
|
}
|
|
566
|
-
// Try to detect domain from last
|
|
572
|
+
// Try to detect domain from the last browser session
|
|
567
573
|
let domain = site;
|
|
568
574
|
try {
|
|
569
|
-
const page = await
|
|
575
|
+
const page = await getBrowserPage();
|
|
570
576
|
const url = await page.getCurrentUrl?.();
|
|
571
577
|
if (url) {
|
|
572
578
|
try {
|
|
@@ -600,7 +606,7 @@ cli({
|
|
|
600
606
|
fs.mkdirSync(dir, { recursive: true });
|
|
601
607
|
fs.writeFileSync(filePath, template, 'utf-8');
|
|
602
608
|
console.log(`Created: ${filePath}`);
|
|
603
|
-
console.log(`Edit the file to implement your adapter, then run: opencli
|
|
609
|
+
console.log(`Edit the file to implement your adapter, then run: opencli browser verify ${name}`);
|
|
604
610
|
}
|
|
605
611
|
catch (err) {
|
|
606
612
|
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -608,7 +614,7 @@ cli({
|
|
|
608
614
|
}
|
|
609
615
|
});
|
|
610
616
|
// ── Verify (test adapter) ──
|
|
611
|
-
|
|
617
|
+
browser.command('verify')
|
|
612
618
|
.argument('<name>', 'Adapter name in site/command format (e.g. hn/top)')
|
|
613
619
|
.description('Execute an adapter and show results')
|
|
614
620
|
.action(async (name) => {
|
|
@@ -630,7 +636,7 @@ cli({
|
|
|
630
636
|
const filePath = path.join(os.homedir(), '.opencli', 'clis', site, `${command}.ts`);
|
|
631
637
|
if (!fs.existsSync(filePath)) {
|
|
632
638
|
console.error(`Adapter not found: ${filePath}`);
|
|
633
|
-
console.error(`Run "opencli
|
|
639
|
+
console.error(`Run "opencli browser init ${name}" to create it.`);
|
|
634
640
|
process.exitCode = EXIT_CODES.GENERIC_ERROR;
|
|
635
641
|
return;
|
|
636
642
|
}
|
|
@@ -641,7 +647,7 @@ cli({
|
|
|
641
647
|
const hasLimitArg = /['"]limit['"]/.test(adapterSrc);
|
|
642
648
|
const limitFlag = hasLimitArg ? ' --limit 3' : '';
|
|
643
649
|
const limitArgs = hasLimitArg ? ['--limit', '3'] : [];
|
|
644
|
-
const invocation =
|
|
650
|
+
const invocation = resolveBrowserVerifyInvocation();
|
|
645
651
|
try {
|
|
646
652
|
const output = execFileSync(invocation.binary, [...invocation.args, site, command, ...limitArgs], {
|
|
647
653
|
cwd: invocation.cwd,
|
|
@@ -657,10 +663,12 @@ cli({
|
|
|
657
663
|
}
|
|
658
664
|
catch (err) {
|
|
659
665
|
console.log(` Executing: opencli ${site} ${command}${limitFlag}\n`);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
if (
|
|
663
|
-
console.
|
|
666
|
+
// execFileSync attaches captured stdout/stderr on its thrown Error.
|
|
667
|
+
const execErr = err;
|
|
668
|
+
if (execErr.stdout)
|
|
669
|
+
console.log(String(execErr.stdout));
|
|
670
|
+
if (execErr.stderr)
|
|
671
|
+
console.error(String(execErr.stderr).slice(0, 500));
|
|
664
672
|
console.log(`\n ✗ Adapter failed. Fix the code and try again.`);
|
|
665
673
|
process.exitCode = EXIT_CODES.GENERIC_ERROR;
|
|
666
674
|
}
|
|
@@ -671,8 +679,8 @@ cli({
|
|
|
671
679
|
}
|
|
672
680
|
});
|
|
673
681
|
// ── Session ──
|
|
674
|
-
|
|
675
|
-
.action(
|
|
682
|
+
browser.command('close').description('Close the automation window')
|
|
683
|
+
.action(browserAction(async (page) => {
|
|
676
684
|
await page.closeWindow?.();
|
|
677
685
|
console.log('Automation window closed');
|
|
678
686
|
}));
|
|
@@ -978,7 +986,7 @@ export function runCli(BUILTIN_CLIS, USER_CLIS) {
|
|
|
978
986
|
createProgram(BUILTIN_CLIS, USER_CLIS).parse();
|
|
979
987
|
}
|
|
980
988
|
export { findPackageRoot };
|
|
981
|
-
export function
|
|
989
|
+
export function resolveBrowserVerifyInvocation(opts = {}) {
|
|
982
990
|
const platform = opts.platform ?? process.platform;
|
|
983
991
|
const fileExists = opts.fileExists ?? fs.existsSync;
|
|
984
992
|
const readFile = opts.readFile ?? ((filePath) => fs.readFileSync(filePath, 'utf-8'));
|
package/dist/src/cli.test.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
const { mockExploreUrl, mockRenderExploreSummary,
|
|
3
|
+
const { mockExploreUrl, mockRenderExploreSummary, mockGenerateVerifiedFromUrl, mockRenderGenerateVerifiedSummary, mockRecordSession, mockRenderRecordSummary, mockCascadeProbe, mockRenderCascadeResult, mockGetBrowserFactory, mockBrowserSession, } = vi.hoisted(() => ({
|
|
4
4
|
mockExploreUrl: vi.fn(),
|
|
5
5
|
mockRenderExploreSummary: vi.fn(),
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
mockGenerateVerifiedFromUrl: vi.fn(),
|
|
7
|
+
mockRenderGenerateVerifiedSummary: vi.fn(),
|
|
8
8
|
mockRecordSession: vi.fn(),
|
|
9
9
|
mockRenderRecordSummary: vi.fn(),
|
|
10
10
|
mockCascadeProbe: vi.fn(),
|
|
@@ -16,9 +16,9 @@ vi.mock('./explore.js', () => ({
|
|
|
16
16
|
exploreUrl: mockExploreUrl,
|
|
17
17
|
renderExploreSummary: mockRenderExploreSummary,
|
|
18
18
|
}));
|
|
19
|
-
vi.mock('./generate.js', () => ({
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
vi.mock('./generate-verified.js', () => ({
|
|
20
|
+
generateVerifiedFromUrl: mockGenerateVerifiedFromUrl,
|
|
21
|
+
renderGenerateVerifiedSummary: mockRenderGenerateVerifiedSummary,
|
|
22
22
|
}));
|
|
23
23
|
vi.mock('./record.js', () => ({
|
|
24
24
|
recordSession: mockRecordSession,
|
|
@@ -32,7 +32,7 @@ vi.mock('./runtime.js', () => ({
|
|
|
32
32
|
getBrowserFactory: mockGetBrowserFactory,
|
|
33
33
|
browserSession: mockBrowserSession,
|
|
34
34
|
}));
|
|
35
|
-
import { createProgram, findPackageRoot,
|
|
35
|
+
import { createProgram, findPackageRoot, resolveBrowserVerifyInvocation } from './cli.js';
|
|
36
36
|
describe('built-in browser commands verbose wiring', () => {
|
|
37
37
|
const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
38
38
|
beforeEach(() => {
|
|
@@ -40,8 +40,8 @@ describe('built-in browser commands verbose wiring', () => {
|
|
|
40
40
|
process.exitCode = undefined;
|
|
41
41
|
mockExploreUrl.mockReset().mockResolvedValue({ ok: true });
|
|
42
42
|
mockRenderExploreSummary.mockReset().mockReturnValue('explore-summary');
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
mockGenerateVerifiedFromUrl.mockReset().mockResolvedValue({ status: 'success' });
|
|
44
|
+
mockRenderGenerateVerifiedSummary.mockReset().mockReturnValue('generate-summary');
|
|
45
45
|
mockRecordSession.mockReset().mockResolvedValue({ candidateCount: 1 });
|
|
46
46
|
mockRenderRecordSummary.mockReset().mockReturnValue('record-summary');
|
|
47
47
|
mockCascadeProbe.mockReset().mockResolvedValue({ ok: true });
|
|
@@ -65,7 +65,12 @@ describe('built-in browser commands verbose wiring', () => {
|
|
|
65
65
|
const program = createProgram('', '');
|
|
66
66
|
await program.parseAsync(['node', 'opencli', 'generate', 'https://example.com', '-v']);
|
|
67
67
|
expect(process.env.OPENCLI_VERBOSE).toBe('1');
|
|
68
|
-
expect(
|
|
68
|
+
expect(mockGenerateVerifiedFromUrl).toHaveBeenCalledWith(expect.objectContaining({ url: 'https://example.com', workspace: 'generate:example.com', noRegister: false }));
|
|
69
|
+
});
|
|
70
|
+
it('passes --no-register through the real CLI command', async () => {
|
|
71
|
+
const program = createProgram('', '');
|
|
72
|
+
await program.parseAsync(['node', 'opencli', 'generate', 'https://example.com', '--no-register']);
|
|
73
|
+
expect(mockGenerateVerifiedFromUrl).toHaveBeenCalledWith(expect.objectContaining({ url: 'https://example.com', workspace: 'generate:example.com', noRegister: true }));
|
|
69
74
|
});
|
|
70
75
|
it('enables OPENCLI_VERBOSE for record via the real CLI command', async () => {
|
|
71
76
|
const program = createProgram('', '');
|
|
@@ -87,13 +92,13 @@ describe('built-in browser commands verbose wiring', () => {
|
|
|
87
92
|
});
|
|
88
93
|
consoleLogSpy.mockClear();
|
|
89
94
|
});
|
|
90
|
-
describe('
|
|
95
|
+
describe('resolveBrowserVerifyInvocation', () => {
|
|
91
96
|
it('prefers the built entry declared in package metadata', () => {
|
|
92
97
|
const projectRoot = path.join('repo-root');
|
|
93
98
|
const exists = new Set([
|
|
94
99
|
path.join(projectRoot, 'dist', 'src', 'main.js'),
|
|
95
100
|
]);
|
|
96
|
-
expect(
|
|
101
|
+
expect(resolveBrowserVerifyInvocation({
|
|
97
102
|
projectRoot,
|
|
98
103
|
readFile: () => JSON.stringify({ bin: { opencli: 'dist/src/main.js' } }),
|
|
99
104
|
fileExists: (candidate) => exists.has(candidate),
|
|
@@ -108,7 +113,7 @@ describe('resolveOperateVerifyInvocation', () => {
|
|
|
108
113
|
const exists = new Set([
|
|
109
114
|
path.join(projectRoot, 'dist', 'src', 'main.js'),
|
|
110
115
|
]);
|
|
111
|
-
expect(
|
|
116
|
+
expect(resolveBrowserVerifyInvocation({
|
|
112
117
|
projectRoot,
|
|
113
118
|
readFile: () => { throw new Error('no package json'); },
|
|
114
119
|
fileExists: (candidate) => exists.has(candidate),
|
|
@@ -124,7 +129,7 @@ describe('resolveOperateVerifyInvocation', () => {
|
|
|
124
129
|
path.join(projectRoot, 'src', 'main.ts'),
|
|
125
130
|
path.join(projectRoot, 'node_modules', '.bin', 'tsx.cmd'),
|
|
126
131
|
]);
|
|
127
|
-
expect(
|
|
132
|
+
expect(resolveBrowserVerifyInvocation({
|
|
128
133
|
projectRoot,
|
|
129
134
|
platform: 'win32',
|
|
130
135
|
fileExists: (candidate) => exists.has(candidate),
|
|
@@ -140,7 +145,7 @@ describe('resolveOperateVerifyInvocation', () => {
|
|
|
140
145
|
const exists = new Set([
|
|
141
146
|
path.join(projectRoot, 'src', 'main.ts'),
|
|
142
147
|
]);
|
|
143
|
-
expect(
|
|
148
|
+
expect(resolveBrowserVerifyInvocation({
|
|
144
149
|
projectRoot,
|
|
145
150
|
platform: 'linux',
|
|
146
151
|
fileExists: (candidate) => exists.has(candidate),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
+
cli({
|
|
3
|
+
site: 'binance',
|
|
4
|
+
name: 'asks',
|
|
5
|
+
description: 'Order book ask prices for a trading pair',
|
|
6
|
+
domain: 'data-api.binance.vision',
|
|
7
|
+
strategy: Strategy.PUBLIC,
|
|
8
|
+
browser: false,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'symbol', type: 'str', required: true, positional: true, help: 'Trading pair symbol (e.g. BTCUSDT, ETHUSDT)' },
|
|
11
|
+
{ name: 'limit', type: 'int', default: 10, help: 'Number of price levels (5, 10, 20, 50, 100)' },
|
|
12
|
+
],
|
|
13
|
+
columns: ['rank', 'ask_price', 'ask_qty'],
|
|
14
|
+
pipeline: [
|
|
15
|
+
{ fetch: { url: 'https://data-api.binance.vision/api/v3/depth?symbol=${{ args.symbol }}&limit=${{ args.limit }}' } },
|
|
16
|
+
{ select: 'asks' },
|
|
17
|
+
{ map: { rank: '${{ index + 1 }}', ask_price: '${{ item.0 }}', ask_qty: '${{ item.1 }}' } },
|
|
18
|
+
{ limit: '${{ args.limit }}' },
|
|
19
|
+
],
|
|
20
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getRegistry } from '@jackwener/opencli/registry';
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { executePipeline } from '../../pipeline/index.js';
|
|
4
|
+
// Import all binance adapters to register them
|
|
5
|
+
import './top.js';
|
|
6
|
+
import './gainers.js';
|
|
7
|
+
import './pairs.js';
|
|
8
|
+
function loadPipeline(name) {
|
|
9
|
+
const cmd = getRegistry().get(`binance/${name}`);
|
|
10
|
+
if (!cmd?.pipeline)
|
|
11
|
+
throw new Error(`Command binance/${name} not found or has no pipeline`);
|
|
12
|
+
return cmd.pipeline;
|
|
13
|
+
}
|
|
14
|
+
function mockJsonOnce(payload) {
|
|
15
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
16
|
+
ok: true,
|
|
17
|
+
status: 200,
|
|
18
|
+
statusText: 'OK',
|
|
19
|
+
json: vi.fn().mockResolvedValue(payload),
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
vi.unstubAllGlobals();
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
});
|
|
26
|
+
describe('binance adapters', () => {
|
|
27
|
+
it('sorts top pairs by numeric quote volume', async () => {
|
|
28
|
+
mockJsonOnce([
|
|
29
|
+
{ symbol: 'SMALL', lastPrice: '1', priceChangePercent: '1.2', highPrice: '1', lowPrice: '1', quoteVolume: '9.9' },
|
|
30
|
+
{ symbol: 'LARGE', lastPrice: '2', priceChangePercent: '2.3', highPrice: '2', lowPrice: '2', quoteVolume: '100.0' },
|
|
31
|
+
{ symbol: 'MID', lastPrice: '3', priceChangePercent: '3.4', highPrice: '3', lowPrice: '3', quoteVolume: '11.0' },
|
|
32
|
+
]);
|
|
33
|
+
const result = await executePipeline(null, loadPipeline('top'), { args: { limit: 3 } });
|
|
34
|
+
expect(result.map((item) => item.symbol)).toEqual(['LARGE', 'MID', 'SMALL']);
|
|
35
|
+
expect(result.map((item) => item.rank)).toEqual([1, 2, 3]);
|
|
36
|
+
});
|
|
37
|
+
it('sorts gainers by numeric percent change', async () => {
|
|
38
|
+
mockJsonOnce([
|
|
39
|
+
{ symbol: 'TEN', lastPrice: '1', priceChangePercent: '10.0', quoteVolume: '100' },
|
|
40
|
+
{ symbol: 'NINE', lastPrice: '1', priceChangePercent: '9.5', quoteVolume: '100' },
|
|
41
|
+
{ symbol: 'HUNDRED', lastPrice: '1', priceChangePercent: '100.0', quoteVolume: '100' },
|
|
42
|
+
]);
|
|
43
|
+
const result = await executePipeline(null, loadPipeline('gainers'), { args: { limit: 3 } });
|
|
44
|
+
expect(result.map((item) => item.symbol)).toEqual(['HUNDRED', 'TEN', 'NINE']);
|
|
45
|
+
});
|
|
46
|
+
it('keeps only TRADING pairs', async () => {
|
|
47
|
+
mockJsonOnce({
|
|
48
|
+
symbols: [
|
|
49
|
+
{ symbol: 'BTCUSDT', baseAsset: 'BTC', quoteAsset: 'USDT', status: 'TRADING' },
|
|
50
|
+
{ symbol: 'OLDPAIR', baseAsset: 'OLD', quoteAsset: 'USDT', status: 'BREAK' },
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
const result = await executePipeline(null, loadPipeline('pairs'), { args: { limit: 10 } });
|
|
54
|
+
expect(result).toEqual([
|
|
55
|
+
{ symbol: 'BTCUSDT', base: 'BTC', quote: 'USDT', status: 'TRADING' },
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
+
cli({
|
|
3
|
+
site: 'binance',
|
|
4
|
+
name: 'depth',
|
|
5
|
+
description: 'Order book bid prices for a trading pair',
|
|
6
|
+
domain: 'data-api.binance.vision',
|
|
7
|
+
strategy: Strategy.PUBLIC,
|
|
8
|
+
browser: false,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'symbol', type: 'str', required: true, positional: true, help: 'Trading pair symbol (e.g. BTCUSDT, ETHUSDT)' },
|
|
11
|
+
{ name: 'limit', type: 'int', default: 10, help: 'Number of price levels (5, 10, 20, 50, 100)' },
|
|
12
|
+
],
|
|
13
|
+
columns: ['rank', 'bid_price', 'bid_qty'],
|
|
14
|
+
pipeline: [
|
|
15
|
+
{ fetch: { url: 'https://data-api.binance.vision/api/v3/depth?symbol=${{ args.symbol }}&limit=${{ args.limit }}' } },
|
|
16
|
+
{ select: 'bids' },
|
|
17
|
+
{ map: { rank: '${{ index + 1 }}', bid_price: '${{ item.0 }}', bid_qty: '${{ item.1 }}' } },
|
|
18
|
+
{ limit: '${{ args.limit }}' },
|
|
19
|
+
],
|
|
20
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
+
cli({
|
|
3
|
+
site: 'binance',
|
|
4
|
+
name: 'gainers',
|
|
5
|
+
description: 'Top gaining trading pairs by 24h price change',
|
|
6
|
+
domain: 'data-api.binance.vision',
|
|
7
|
+
strategy: Strategy.PUBLIC,
|
|
8
|
+
browser: false,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'limit', type: 'int', default: 10, help: 'Number of trading pairs' },
|
|
11
|
+
],
|
|
12
|
+
columns: ['rank', 'symbol', 'price', 'change_24h', 'volume'],
|
|
13
|
+
pipeline: [
|
|
14
|
+
{ fetch: { url: 'https://data-api.binance.vision/api/v3/ticker/24hr' } },
|
|
15
|
+
{ filter: 'item.priceChangePercent' },
|
|
16
|
+
{ map: { symbol: '${{ item.symbol }}', price: '${{ item.lastPrice }}', change_24h: '${{ item.priceChangePercent }}', volume: '${{ item.quoteVolume }}', sort_change: '${{ Number(item.priceChangePercent) }}' } },
|
|
17
|
+
{ sort: { by: 'sort_change', order: 'desc' } },
|
|
18
|
+
{ map: { rank: '${{ index + 1 }}', symbol: '${{ item.symbol }}', price: '${{ item.lastPrice }}', change_24h: '${{ item.priceChangePercent }}', volume: '${{ item.quoteVolume }}' } },
|
|
19
|
+
{ limit: '${{ args.limit }}' },
|
|
20
|
+
],
|
|
21
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
2
|
+
cli({
|
|
3
|
+
site: 'binance',
|
|
4
|
+
name: 'klines',
|
|
5
|
+
description: 'Candlestick/kline data for a trading pair',
|
|
6
|
+
domain: 'data-api.binance.vision',
|
|
7
|
+
strategy: Strategy.PUBLIC,
|
|
8
|
+
browser: false,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'symbol', type: 'str', required: true, positional: true, help: 'Trading pair symbol (e.g. BTCUSDT, ETHUSDT)' },
|
|
11
|
+
{ name: 'interval', type: 'str', default: '1d', help: 'Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)' },
|
|
12
|
+
{ name: 'limit', type: 'int', default: 10, help: 'Number of klines (max 1000)' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['open', 'high', 'low', 'close', 'volume'],
|
|
15
|
+
pipeline: [
|
|
16
|
+
{ fetch: { url: 'https://data-api.binance.vision/api/v3/klines?symbol=${{ args.symbol }}&interval=${{ args.interval }}&limit=${{ args.limit }}' } },
|
|
17
|
+
{ map: { open: '${{ item.1 }}', high: '${{ item.2 }}', low: '${{ item.3 }}', close: '${{ item.4 }}', volume: '${{ item.5 }}' } },
|
|
18
|
+
{ limit: '${{ args.limit }}' },
|
|
19
|
+
],
|
|
20
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|