@jackwener/opencli 1.1.1 → 1.2.0
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/CONTRIBUTING.md +39 -1
- package/README.md +9 -10
- package/README.zh-CN.md +39 -17
- package/SKILL.md +10 -5
- package/dist/browser/cdp.d.ts +4 -4
- package/dist/browser/cdp.js +39 -16
- package/dist/browser/daemon-client.d.ts +2 -1
- package/dist/browser/dom-helpers.js +38 -7
- package/dist/browser/dom-snapshot.d.ts +86 -0
- package/dist/browser/dom-snapshot.js +729 -0
- package/dist/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/browser/dom-snapshot.test.js +212 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/page.d.ts +14 -24
- package/dist/browser/page.js +37 -4
- package/dist/build-manifest.d.ts +11 -4
- package/dist/build-manifest.js +59 -21
- package/dist/build-manifest.test.js +58 -2
- package/dist/cli-manifest.json +3856 -1509
- package/dist/cli.js +66 -0
- package/dist/clis/barchart/greeks.js +1 -1
- package/dist/clis/barchart/options.js +1 -1
- package/dist/clis/barchart/quote.js +1 -1
- package/dist/clis/bilibili/download.js +1 -1
- package/dist/clis/bilibili/following.js +1 -1
- package/dist/clis/bilibili/subtitle.js +1 -1
- package/dist/clis/bilibili/user-videos.js +1 -1
- package/dist/clis/boss/batchgreet.js +10 -97
- package/dist/clis/boss/chatlist.js +8 -25
- package/dist/clis/boss/chatmsg.js +11 -42
- package/dist/clis/boss/common.d.ts +92 -0
- package/dist/clis/boss/common.js +223 -0
- package/dist/clis/boss/detail.js +7 -49
- package/dist/clis/boss/exchange.js +13 -79
- package/dist/clis/boss/greet.js +18 -145
- package/dist/clis/boss/invite.js +26 -121
- package/dist/clis/boss/joblist.js +6 -31
- package/dist/clis/boss/mark.js +12 -85
- package/dist/clis/boss/recommend.js +10 -49
- package/dist/clis/boss/resume.js +18 -118
- package/dist/clis/boss/search.js +12 -60
- package/dist/clis/boss/send.js +17 -151
- package/dist/clis/boss/stats.js +18 -69
- package/dist/clis/coupang/add-to-cart.js +1 -1
- package/dist/clis/devto/tag.yaml +34 -0
- package/dist/clis/devto/top.yaml +29 -0
- package/dist/clis/devto/user.yaml +33 -0
- package/dist/clis/douban/book-hot.d.ts +1 -0
- package/dist/clis/douban/book-hot.js +14 -0
- package/dist/clis/douban/marks.d.ts +1 -0
- package/dist/clis/douban/marks.js +115 -0
- package/dist/clis/douban/movie-hot.d.ts +1 -0
- package/dist/clis/douban/movie-hot.js +14 -0
- package/dist/clis/douban/reviews.d.ts +1 -0
- package/dist/clis/douban/reviews.js +106 -0
- package/dist/clis/douban/search.d.ts +1 -0
- package/dist/clis/douban/search.js +16 -0
- package/dist/clis/douban/shared.d.ts +4 -0
- package/dist/clis/douban/shared.js +155 -0
- package/dist/clis/douban/subject.yaml +76 -0
- package/dist/clis/douban/top250.yaml +70 -0
- package/dist/clis/douban/utils.d.ts +35 -0
- package/dist/clis/douban/utils.js +48 -0
- package/dist/clis/facebook/add-friend.yaml +43 -0
- package/dist/clis/facebook/events.yaml +44 -0
- package/dist/clis/facebook/feed.yaml +63 -0
- package/dist/clis/facebook/friends.yaml +42 -0
- package/dist/clis/facebook/groups.yaml +50 -0
- package/dist/clis/facebook/join-group.yaml +44 -0
- package/dist/clis/facebook/memories.yaml +39 -0
- package/dist/clis/facebook/notifications.yaml +40 -0
- package/dist/clis/facebook/profile.yaml +37 -0
- package/dist/clis/facebook/search.yaml +46 -0
- package/dist/clis/google/news.d.ts +5 -0
- package/dist/clis/google/news.js +58 -0
- package/dist/clis/google/search.d.ts +10 -0
- package/dist/clis/google/search.js +127 -0
- package/dist/clis/google/suggest.d.ts +5 -0
- package/dist/clis/google/suggest.js +34 -0
- package/dist/clis/google/trends.d.ts +5 -0
- package/dist/clis/google/trends.js +38 -0
- package/dist/clis/google/utils.d.ts +9 -0
- package/dist/clis/google/utils.js +23 -0
- package/dist/clis/google/utils.test.d.ts +1 -0
- package/dist/clis/google/utils.test.js +75 -0
- package/dist/clis/grok/ask.d.ts +14 -0
- package/dist/clis/grok/ask.js +257 -65
- package/dist/clis/grok/ask.test.d.ts +1 -0
- package/dist/clis/grok/ask.test.js +36 -0
- package/dist/clis/instagram/comment.yaml +52 -0
- package/dist/clis/instagram/explore.yaml +43 -0
- package/dist/clis/instagram/follow.yaml +41 -0
- package/dist/clis/instagram/followers.yaml +51 -0
- package/dist/clis/instagram/following.yaml +51 -0
- package/dist/clis/instagram/like.yaml +46 -0
- package/dist/clis/instagram/profile.yaml +42 -0
- package/dist/clis/instagram/save.yaml +46 -0
- package/dist/clis/instagram/saved.yaml +40 -0
- package/dist/clis/instagram/search.yaml +43 -0
- package/dist/clis/instagram/unfollow.yaml +38 -0
- package/dist/clis/instagram/unlike.yaml +46 -0
- package/dist/clis/instagram/unsave.yaml +46 -0
- package/dist/clis/instagram/user.yaml +54 -0
- package/dist/clis/jike/repost.js +1 -1
- package/dist/clis/jimeng/generate.yaml +1 -0
- package/dist/clis/linux-do/category.yaml +1 -0
- package/dist/clis/lobsters/active.yaml +29 -0
- package/dist/clis/lobsters/hot.yaml +29 -0
- package/dist/clis/lobsters/newest.yaml +29 -0
- package/dist/clis/lobsters/tag.yaml +34 -0
- package/dist/clis/medium/feed.d.ts +1 -0
- package/dist/clis/medium/feed.js +15 -0
- package/dist/clis/medium/search.d.ts +1 -0
- package/dist/clis/medium/search.js +15 -0
- package/dist/clis/medium/shared.d.ts +5 -0
- package/dist/clis/medium/shared.js +78 -0
- package/dist/clis/medium/user.d.ts +1 -0
- package/dist/clis/medium/user.js +15 -0
- package/dist/clis/reddit/comment.js +1 -1
- package/dist/clis/reddit/read.js +1 -1
- package/dist/clis/reddit/save.js +1 -1
- package/dist/clis/reddit/subreddit.yaml +1 -0
- package/dist/clis/reddit/subscribe.js +1 -1
- package/dist/clis/reddit/upvote.js +1 -1
- package/dist/clis/sinablog/article.d.ts +1 -0
- package/dist/clis/sinablog/article.js +14 -0
- package/dist/clis/sinablog/hot.d.ts +1 -0
- package/dist/clis/sinablog/hot.js +14 -0
- package/dist/clis/sinablog/search.d.ts +1 -0
- package/dist/clis/sinablog/search.js +51 -0
- package/dist/clis/sinablog/shared.d.ts +7 -0
- package/dist/clis/sinablog/shared.js +187 -0
- package/dist/clis/sinablog/user.d.ts +1 -0
- package/dist/clis/sinablog/user.js +15 -0
- package/dist/clis/substack/feed.d.ts +1 -0
- package/dist/clis/substack/feed.js +15 -0
- package/dist/clis/substack/publication.d.ts +1 -0
- package/dist/clis/substack/publication.js +15 -0
- package/dist/clis/substack/search.d.ts +1 -0
- package/dist/clis/substack/search.js +77 -0
- package/dist/clis/substack/shared.d.ts +4 -0
- package/dist/clis/substack/shared.js +129 -0
- package/dist/clis/tiktok/comment.yaml +66 -0
- package/dist/clis/tiktok/explore.yaml +39 -0
- package/dist/clis/tiktok/follow.yaml +39 -0
- package/dist/clis/tiktok/following.yaml +46 -0
- package/dist/clis/tiktok/friends.yaml +47 -0
- package/dist/clis/tiktok/like.yaml +38 -0
- package/dist/clis/tiktok/live.yaml +51 -0
- package/dist/clis/tiktok/notifications.yaml +52 -0
- package/dist/clis/tiktok/profile.yaml +45 -0
- package/dist/clis/tiktok/save.yaml +34 -0
- package/dist/clis/tiktok/search.yaml +46 -0
- package/dist/clis/tiktok/unfollow.yaml +44 -0
- package/dist/clis/tiktok/unlike.yaml +38 -0
- package/dist/clis/tiktok/unsave.yaml +36 -0
- package/dist/clis/tiktok/user.yaml +44 -0
- package/dist/clis/twitter/download.d.ts +1 -1
- package/dist/clis/twitter/download.js +3 -3
- package/dist/clis/twitter/followers.js +1 -1
- package/dist/clis/twitter/following.js +1 -1
- package/dist/clis/twitter/thread.js +1 -1
- package/dist/clis/twitter/timeline.d.ts +23 -0
- package/dist/clis/twitter/timeline.js +42 -14
- package/dist/clis/twitter/timeline.test.d.ts +1 -0
- package/dist/clis/twitter/timeline.test.js +102 -0
- package/dist/clis/wikipedia/random.d.ts +1 -0
- package/dist/clis/wikipedia/random.js +19 -0
- package/dist/clis/wikipedia/search.js +3 -3
- package/dist/clis/wikipedia/summary.js +4 -9
- package/dist/clis/wikipedia/trending.d.ts +1 -0
- package/dist/clis/wikipedia/trending.js +35 -0
- package/dist/clis/wikipedia/utils.d.ts +28 -0
- package/dist/clis/wikipedia/utils.js +13 -0
- package/dist/clis/xiaohongshu/creator-note-detail.js +1 -1
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +2 -0
- package/dist/clis/xiaohongshu/creator-notes.test.js +2 -0
- package/dist/clis/xiaohongshu/download.js +1 -1
- package/dist/clis/xueqiu/earnings-date.yaml +69 -0
- package/dist/clis/xueqiu/search.yaml +2 -1
- package/dist/clis/xueqiu/stock.yaml +2 -0
- package/dist/clis/yahoo-finance/quote.js +1 -1
- package/dist/commanderAdapter.js +13 -7
- package/dist/discovery.d.ts +8 -0
- package/dist/discovery.js +105 -19
- package/dist/doctor.js +3 -1
- package/dist/doctor.test.js +46 -2
- package/dist/engine.test.d.ts +0 -3
- package/dist/engine.test.js +74 -6
- package/dist/execution.d.ts +4 -2
- package/dist/execution.js +31 -7
- package/dist/explore.d.ts +76 -3
- package/dist/explore.js +11 -4
- package/dist/generate.d.ts +41 -2
- package/dist/generate.js +5 -4
- package/dist/main.js +2 -1
- package/dist/pipeline/executor.d.ts +2 -2
- package/dist/pipeline/executor.js +2 -2
- package/dist/pipeline/executor.test.js +33 -6
- package/dist/pipeline/registry.d.ts +1 -1
- package/dist/pipeline/steps/browser.d.ts +7 -7
- package/dist/pipeline/steps/browser.js +15 -7
- package/dist/pipeline/steps/fetch.d.ts +1 -1
- package/dist/pipeline/steps/fetch.js +11 -7
- package/dist/pipeline/steps/transform.d.ts +6 -5
- package/dist/pipeline/steps/transform.js +30 -9
- package/dist/pipeline/template.d.ts +6 -6
- package/dist/pipeline/template.js +43 -5
- package/dist/pipeline/template.test.js +18 -0
- package/dist/pipeline/transform.test.js +11 -0
- package/dist/plugin.d.ts +31 -0
- package/dist/plugin.js +216 -0
- package/dist/plugin.test.d.ts +4 -0
- package/dist/plugin.test.js +76 -0
- package/dist/registry-api.d.ts +11 -0
- package/dist/registry-api.js +9 -0
- package/dist/registry.d.ts +11 -0
- package/dist/registry.js +6 -1
- package/dist/synthesize.d.ts +94 -4
- package/dist/synthesize.js +5 -4
- package/dist/types.d.ts +39 -26
- package/dist/validate.js +8 -2
- package/docs/.vitepress/config.mts +6 -4
- package/docs/adapters/browser/barchart.md +6 -5
- package/docs/adapters/browser/bilibili.md +9 -0
- package/docs/adapters/browser/devto.md +35 -0
- package/docs/adapters/browser/douban.md +38 -0
- package/docs/adapters/browser/facebook.md +36 -0
- package/docs/adapters/browser/google.md +62 -0
- package/docs/adapters/browser/grok.md +26 -8
- package/docs/adapters/browser/instagram.md +46 -0
- package/docs/adapters/browser/lobsters.md +32 -0
- package/docs/adapters/browser/medium.md +32 -0
- package/docs/adapters/browser/reddit.md +9 -0
- package/docs/adapters/browser/sinablog.md +36 -0
- package/docs/adapters/browser/substack.md +38 -0
- package/docs/adapters/browser/tiktok.md +68 -0
- package/docs/adapters/browser/wikipedia.md +11 -2
- package/docs/adapters/browser/xueqiu.md +10 -0
- package/docs/adapters/browser/yahoo-finance.md +6 -5
- package/docs/adapters/desktop/antigravity.md +6 -0
- package/docs/adapters/desktop/chatgpt.md +2 -1
- package/docs/adapters/desktop/codex.md +5 -1
- package/docs/adapters/desktop/cursor.md +4 -0
- package/docs/adapters/desktop/discord.md +7 -7
- package/docs/adapters/index.md +1 -4
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +153 -0
- package/docs/zh/guide/plugins.md +107 -0
- package/extension/src/background.ts +18 -11
- package/package.json +10 -5
- package/scripts/clean-dist.cjs +13 -0
- package/src/browser/cdp.ts +71 -31
- package/src/browser/daemon-client.ts +2 -1
- package/src/browser/dom-helpers.ts +38 -7
- package/src/browser/dom-snapshot.test.ts +249 -0
- package/src/browser/dom-snapshot.ts +770 -0
- package/src/browser/index.ts +2 -0
- package/src/browser/page.ts +50 -19
- package/src/build-manifest.test.ts +70 -2
- package/src/build-manifest.ts +94 -26
- package/src/cli.ts +71 -2
- package/src/clis/barchart/greeks.ts +1 -1
- package/src/clis/barchart/options.ts +1 -1
- package/src/clis/barchart/quote.ts +1 -1
- package/src/clis/bilibili/download.ts +1 -1
- package/src/clis/bilibili/following.ts +1 -1
- package/src/clis/bilibili/subtitle.ts +1 -1
- package/src/clis/bilibili/user-videos.ts +1 -1
- package/src/clis/boss/batchgreet.ts +14 -106
- package/src/clis/boss/chatlist.ts +12 -26
- package/src/clis/boss/chatmsg.ts +16 -40
- package/src/clis/boss/common.ts +287 -0
- package/src/clis/boss/detail.ts +8 -54
- package/src/clis/boss/exchange.ts +15 -89
- package/src/clis/boss/greet.ts +23 -160
- package/src/clis/boss/invite.ts +36 -133
- package/src/clis/boss/joblist.ts +7 -36
- package/src/clis/boss/mark.ts +13 -94
- package/src/clis/boss/recommend.ts +12 -57
- package/src/clis/boss/resume.ts +19 -124
- package/src/clis/boss/search.ts +13 -66
- package/src/clis/boss/send.ts +21 -161
- package/src/clis/boss/stats.ts +19 -74
- package/src/clis/coupang/add-to-cart.ts +1 -1
- package/src/clis/devto/tag.yaml +34 -0
- package/src/clis/devto/top.yaml +29 -0
- package/src/clis/devto/user.yaml +33 -0
- package/src/clis/douban/book-hot.ts +15 -0
- package/src/clis/douban/marks.ts +135 -0
- package/src/clis/douban/movie-hot.ts +15 -0
- package/src/clis/douban/reviews.ts +127 -0
- package/src/clis/douban/search.ts +17 -0
- package/src/clis/douban/shared.ts +165 -0
- package/src/clis/douban/subject.yaml +76 -0
- package/src/clis/douban/top250.yaml +70 -0
- package/src/clis/douban/utils.ts +81 -0
- package/src/clis/facebook/add-friend.yaml +43 -0
- package/src/clis/facebook/events.yaml +44 -0
- package/src/clis/facebook/feed.yaml +63 -0
- package/src/clis/facebook/friends.yaml +42 -0
- package/src/clis/facebook/groups.yaml +50 -0
- package/src/clis/facebook/join-group.yaml +44 -0
- package/src/clis/facebook/memories.yaml +39 -0
- package/src/clis/facebook/notifications.yaml +40 -0
- package/src/clis/facebook/profile.yaml +37 -0
- package/src/clis/facebook/search.yaml +46 -0
- package/src/clis/google/news.ts +66 -0
- package/src/clis/google/search.ts +133 -0
- package/src/clis/google/suggest.ts +40 -0
- package/src/clis/google/trends.ts +44 -0
- package/src/clis/google/utils.test.ts +82 -0
- package/src/clis/google/utils.ts +24 -0
- package/src/clis/grok/ask.test.ts +53 -0
- package/src/clis/grok/ask.ts +300 -69
- package/src/clis/instagram/comment.yaml +52 -0
- package/src/clis/instagram/explore.yaml +43 -0
- package/src/clis/instagram/follow.yaml +41 -0
- package/src/clis/instagram/followers.yaml +51 -0
- package/src/clis/instagram/following.yaml +51 -0
- package/src/clis/instagram/like.yaml +46 -0
- package/src/clis/instagram/profile.yaml +42 -0
- package/src/clis/instagram/save.yaml +46 -0
- package/src/clis/instagram/saved.yaml +40 -0
- package/src/clis/instagram/search.yaml +43 -0
- package/src/clis/instagram/unfollow.yaml +38 -0
- package/src/clis/instagram/unlike.yaml +46 -0
- package/src/clis/instagram/unsave.yaml +46 -0
- package/src/clis/instagram/user.yaml +54 -0
- package/src/clis/jike/repost.ts +1 -1
- package/src/clis/jimeng/generate.yaml +1 -0
- package/src/clis/linux-do/category.yaml +1 -0
- package/src/clis/lobsters/active.yaml +29 -0
- package/src/clis/lobsters/hot.yaml +29 -0
- package/src/clis/lobsters/newest.yaml +29 -0
- package/src/clis/lobsters/tag.yaml +34 -0
- package/src/clis/medium/feed.ts +16 -0
- package/src/clis/medium/search.ts +16 -0
- package/src/clis/medium/shared.ts +83 -0
- package/src/clis/medium/user.ts +16 -0
- package/src/clis/reddit/comment.ts +1 -1
- package/src/clis/reddit/read.ts +1 -1
- package/src/clis/reddit/save.ts +1 -1
- package/src/clis/reddit/subreddit.yaml +1 -0
- package/src/clis/reddit/subscribe.ts +1 -1
- package/src/clis/reddit/upvote.ts +1 -1
- package/src/clis/sinablog/article.ts +15 -0
- package/src/clis/sinablog/hot.ts +15 -0
- package/src/clis/sinablog/search.ts +56 -0
- package/src/clis/sinablog/shared.ts +198 -0
- package/src/clis/sinablog/user.ts +16 -0
- package/src/clis/substack/feed.ts +16 -0
- package/src/clis/substack/publication.ts +16 -0
- package/src/clis/substack/search.ts +91 -0
- package/src/clis/substack/shared.ts +132 -0
- package/src/clis/tiktok/comment.yaml +66 -0
- package/src/clis/tiktok/explore.yaml +39 -0
- package/src/clis/tiktok/follow.yaml +39 -0
- package/src/clis/tiktok/following.yaml +46 -0
- package/src/clis/tiktok/friends.yaml +47 -0
- package/src/clis/tiktok/like.yaml +38 -0
- package/src/clis/tiktok/live.yaml +51 -0
- package/src/clis/tiktok/notifications.yaml +52 -0
- package/src/clis/tiktok/profile.yaml +45 -0
- package/src/clis/tiktok/save.yaml +34 -0
- package/src/clis/tiktok/search.yaml +46 -0
- package/src/clis/tiktok/unfollow.yaml +44 -0
- package/src/clis/tiktok/unlike.yaml +38 -0
- package/src/clis/tiktok/unsave.yaml +36 -0
- package/src/clis/tiktok/user.yaml +44 -0
- package/src/clis/twitter/download.ts +3 -3
- package/src/clis/twitter/followers.ts +1 -1
- package/src/clis/twitter/following.ts +1 -1
- package/src/clis/twitter/thread.ts +1 -1
- package/src/clis/twitter/timeline.test.ts +109 -0
- package/src/clis/twitter/timeline.ts +59 -19
- package/src/clis/wikipedia/random.ts +19 -0
- package/src/clis/wikipedia/search.ts +10 -4
- package/src/clis/wikipedia/summary.ts +4 -9
- package/src/clis/wikipedia/trending.ts +41 -0
- package/src/clis/wikipedia/utils.ts +31 -0
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +2 -0
- package/src/clis/xiaohongshu/creator-note-detail.ts +1 -1
- package/src/clis/xiaohongshu/creator-notes.test.ts +2 -0
- package/src/clis/xiaohongshu/download.ts +1 -1
- package/src/clis/xueqiu/earnings-date.yaml +69 -0
- package/src/clis/xueqiu/search.yaml +2 -1
- package/src/clis/xueqiu/stock.yaml +2 -0
- package/src/clis/yahoo-finance/quote.ts +1 -1
- package/src/commanderAdapter.ts +17 -10
- package/src/discovery.ts +134 -24
- package/src/doctor.test.ts +59 -2
- package/src/doctor.ts +4 -2
- package/src/engine.test.ts +79 -6
- package/src/execution.ts +42 -16
- package/src/explore.ts +77 -9
- package/src/generate.ts +58 -9
- package/src/main.ts +2 -1
- package/src/pipeline/executor.test.ts +35 -6
- package/src/pipeline/executor.ts +11 -7
- package/src/pipeline/registry.ts +3 -3
- package/src/pipeline/steps/browser.ts +24 -15
- package/src/pipeline/steps/fetch.ts +18 -13
- package/src/pipeline/steps/transform.ts +40 -15
- package/src/pipeline/template.test.ts +18 -0
- package/src/pipeline/template.ts +86 -13
- package/src/pipeline/transform.test.ts +15 -2
- package/src/plugin.test.ts +86 -0
- package/src/plugin.ts +254 -0
- package/src/registry-api.ts +12 -0
- package/src/registry.ts +19 -1
- package/src/synthesize.ts +102 -21
- package/src/types.ts +44 -12
- package/src/validate.ts +19 -4
- package/tests/e2e/browser-public.test.ts +11 -0
- package/tests/e2e/public-commands.test.ts +64 -0
- package/dist/clis/feishu/new.d.ts +0 -1
- package/dist/clis/feishu/new.js +0 -27
- package/dist/clis/feishu/read.d.ts +0 -1
- package/dist/clis/feishu/read.js +0 -40
- package/dist/clis/feishu/search.d.ts +0 -1
- package/dist/clis/feishu/search.js +0 -30
- package/dist/clis/feishu/send.d.ts +0 -1
- package/dist/clis/feishu/send.js +0 -39
- package/dist/clis/feishu/status.d.ts +0 -1
- package/dist/clis/feishu/status.js +0 -28
- package/dist/clis/neteasemusic/like.d.ts +0 -1
- package/dist/clis/neteasemusic/like.js +0 -25
- package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
- package/dist/clis/neteasemusic/lyrics.js +0 -47
- package/dist/clis/neteasemusic/next.d.ts +0 -1
- package/dist/clis/neteasemusic/next.js +0 -26
- package/dist/clis/neteasemusic/play.d.ts +0 -1
- package/dist/clis/neteasemusic/play.js +0 -26
- package/dist/clis/neteasemusic/playing.d.ts +0 -1
- package/dist/clis/neteasemusic/playing.js +0 -59
- package/dist/clis/neteasemusic/playlist.d.ts +0 -1
- package/dist/clis/neteasemusic/playlist.js +0 -46
- package/dist/clis/neteasemusic/prev.d.ts +0 -1
- package/dist/clis/neteasemusic/prev.js +0 -25
- package/dist/clis/neteasemusic/search.d.ts +0 -1
- package/dist/clis/neteasemusic/search.js +0 -52
- package/dist/clis/neteasemusic/status.d.ts +0 -1
- package/dist/clis/neteasemusic/status.js +0 -16
- package/dist/clis/neteasemusic/volume.d.ts +0 -1
- package/dist/clis/neteasemusic/volume.js +0 -54
- package/dist/clis/wechat/chats.d.ts +0 -1
- package/dist/clis/wechat/chats.js +0 -28
- package/dist/clis/wechat/contacts.d.ts +0 -1
- package/dist/clis/wechat/contacts.js +0 -28
- package/dist/clis/wechat/read.d.ts +0 -1
- package/dist/clis/wechat/read.js +0 -58
- package/dist/clis/wechat/search.d.ts +0 -1
- package/dist/clis/wechat/search.js +0 -31
- package/dist/clis/wechat/send.d.ts +0 -1
- package/dist/clis/wechat/send.js +0 -42
- package/dist/clis/wechat/status.d.ts +0 -1
- package/dist/clis/wechat/status.js +0 -29
- package/dist/pipeline.d.ts +0 -7
- package/dist/pipeline.js +0 -7
- package/docs/adapters/browser/github.md +0 -26
- package/docs/adapters/desktop/feishu.md +0 -20
- package/docs/adapters/desktop/neteasemusic.md +0 -31
- package/docs/adapters/desktop/wechat.md +0 -28
- package/src/clis/antigravity/README.md +0 -5
- package/src/clis/antigravity/README.zh-CN.md +0 -51
- package/src/clis/chaoxing/README.md +0 -14
- package/src/clis/chaoxing/README.zh-CN.md +0 -35
- package/src/clis/chatgpt/README.md +0 -5
- package/src/clis/chatgpt/README.zh-CN.md +0 -44
- package/src/clis/chatwise/README.md +0 -5
- package/src/clis/chatwise/README.zh-CN.md +0 -38
- package/src/clis/codex/README.md +0 -5
- package/src/clis/codex/README.zh-CN.md +0 -33
- package/src/clis/cursor/README.md +0 -5
- package/src/clis/cursor/README.zh-CN.md +0 -33
- package/src/clis/discord-app/README.md +0 -5
- package/src/clis/discord-app/README.zh-CN.md +0 -28
- package/src/clis/feishu/README.md +0 -5
- package/src/clis/feishu/README.zh-CN.md +0 -20
- package/src/clis/feishu/new.ts +0 -32
- package/src/clis/feishu/read.ts +0 -48
- package/src/clis/feishu/search.ts +0 -35
- package/src/clis/feishu/send.ts +0 -46
- package/src/clis/feishu/status.ts +0 -34
- package/src/clis/neteasemusic/README.md +0 -5
- package/src/clis/neteasemusic/README.zh-CN.md +0 -31
- package/src/clis/neteasemusic/like.ts +0 -28
- package/src/clis/neteasemusic/lyrics.ts +0 -53
- package/src/clis/neteasemusic/next.ts +0 -30
- package/src/clis/neteasemusic/play.ts +0 -30
- package/src/clis/neteasemusic/playing.ts +0 -62
- package/src/clis/neteasemusic/playlist.ts +0 -51
- package/src/clis/neteasemusic/prev.ts +0 -29
- package/src/clis/neteasemusic/search.ts +0 -58
- package/src/clis/neteasemusic/status.ts +0 -18
- package/src/clis/neteasemusic/volume.ts +0 -61
- package/src/clis/notion/README.md +0 -5
- package/src/clis/notion/README.zh-CN.md +0 -29
- package/src/clis/wechat/README.md +0 -5
- package/src/clis/wechat/README.zh-CN.md +0 -28
- package/src/clis/wechat/chats.ts +0 -33
- package/src/clis/wechat/contacts.ts +0 -33
- package/src/clis/wechat/read.ts +0 -72
- package/src/clis/wechat/search.ts +0 -36
- package/src/clis/wechat/send.ts +0 -49
- package/src/clis/wechat/status.ts +0 -35
- package/src/pipeline.ts +0 -8
package/src/browser/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ export { Page } from './page.js';
|
|
|
9
9
|
export { BrowserBridge, BrowserBridge as PlaywrightMCP } from './mcp.js';
|
|
10
10
|
export { CDPBridge } from './cdp.js';
|
|
11
11
|
export { isDaemonRunning } from './daemon-client.js';
|
|
12
|
+
export { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
|
|
13
|
+
export type { SnapshotOptions } from './dom-snapshot.js';
|
|
12
14
|
|
|
13
15
|
import { extractTabEntries, diffTabIndexes, appendLimited } from './tabs.js';
|
|
14
16
|
import { __test__ as cdpTest } from './cdp.js';
|
package/src/browser/page.ts
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { formatSnapshot } from '../snapshotFormatter.js';
|
|
14
|
-
import type { IPage } from '../types.js';
|
|
14
|
+
import type { BrowserCookie, IPage, ScreenshotOptions, SnapshotOptions, WaitOptions } from '../types.js';
|
|
15
15
|
import { sendCommand } from './daemon-client.js';
|
|
16
16
|
import { wrapForEval } from './utils.js';
|
|
17
|
+
import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
|
|
17
18
|
import {
|
|
18
19
|
clickJs,
|
|
19
20
|
typeTextJs,
|
|
@@ -69,17 +70,40 @@ export class Page implements IPage {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
async evaluate(js: string): Promise<
|
|
73
|
+
async evaluate(js: string): Promise<unknown> {
|
|
73
74
|
const code = wrapForEval(js);
|
|
74
75
|
return sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
async getCookies(opts: { domain?: string; url?: string } = {}): Promise<
|
|
78
|
+
async getCookies(opts: { domain?: string; url?: string } = {}): Promise<BrowserCookie[]> {
|
|
78
79
|
const result = await sendCommand('cookies', { ...this._workspaceOpt(), ...opts });
|
|
79
80
|
return Array.isArray(result) ? result : [];
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
async snapshot(opts:
|
|
83
|
+
async snapshot(opts: SnapshotOptions = {}): Promise<unknown> {
|
|
84
|
+
// Primary: use the advanced DOM snapshot engine with multi-layer pruning
|
|
85
|
+
const snapshotJs = generateSnapshotJs({
|
|
86
|
+
viewportExpand: opts.viewportExpand ?? 800,
|
|
87
|
+
maxDepth: Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200)),
|
|
88
|
+
interactiveOnly: opts.interactive ?? false,
|
|
89
|
+
maxTextLength: opts.maxTextLength ?? 120,
|
|
90
|
+
includeScrollInfo: true,
|
|
91
|
+
bboxDedup: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const result = await sendCommand('exec', { code: snapshotJs, ...this._workspaceOpt(), ...this._tabOpt() });
|
|
96
|
+
// The advanced engine already produces a clean, pruned, LLM-friendly output.
|
|
97
|
+
// Do NOT pass through formatSnapshot — its format is incompatible.
|
|
98
|
+
return result;
|
|
99
|
+
} catch {
|
|
100
|
+
// Fallback: basic DOM snapshot (original implementation)
|
|
101
|
+
return this._basicSnapshot(opts);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Fallback basic snapshot — original buildTree approach */
|
|
106
|
+
private async _basicSnapshot(opts: Pick<SnapshotOptions, 'interactive' | 'compact' | 'maxDepth' | 'raw'> = {}): Promise<unknown> {
|
|
83
107
|
const maxDepth = Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200));
|
|
84
108
|
const code = `
|
|
85
109
|
(async () => {
|
|
@@ -93,7 +117,7 @@ export class Page implements IPage {
|
|
|
93
117
|
|
|
94
118
|
let indent = ' '.repeat(depth);
|
|
95
119
|
let line = indent + role;
|
|
96
|
-
if (name) line += ' "' + name.replace(/"/g, '
|
|
120
|
+
if (name) line += ' "' + name.replace(/"/g, '\\\\\\"') + '"';
|
|
97
121
|
if (node.tagName?.toLowerCase() === 'a' && node.href) line += ' [' + node.href + ']';
|
|
98
122
|
if (node.tagName?.toLowerCase() === 'input') line += ' [' + (node.type || 'text') + ']';
|
|
99
123
|
|
|
@@ -129,7 +153,17 @@ export class Page implements IPage {
|
|
|
129
153
|
await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
|
|
130
154
|
}
|
|
131
155
|
|
|
132
|
-
async
|
|
156
|
+
async scrollTo(ref: string): Promise<unknown> {
|
|
157
|
+
const code = scrollToRefJs(ref);
|
|
158
|
+
return sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async getFormState(): Promise<Record<string, unknown>> {
|
|
162
|
+
const code = getFormStateJs();
|
|
163
|
+
return (await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() })) as Record<string, unknown>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async wait(options: number | WaitOptions): Promise<void> {
|
|
133
167
|
if (typeof options === 'number') {
|
|
134
168
|
await new Promise(resolve => setTimeout(resolve, options * 1000));
|
|
135
169
|
return;
|
|
@@ -145,8 +179,9 @@ export class Page implements IPage {
|
|
|
145
179
|
}
|
|
146
180
|
}
|
|
147
181
|
|
|
148
|
-
async tabs(): Promise<
|
|
149
|
-
|
|
182
|
+
async tabs(): Promise<unknown[]> {
|
|
183
|
+
const result = await sendCommand('tabs', { op: 'list', ...this._workspaceOpt() });
|
|
184
|
+
return Array.isArray(result) ? result : [];
|
|
150
185
|
}
|
|
151
186
|
|
|
152
187
|
async closeTab(index?: number): Promise<void> {
|
|
@@ -161,9 +196,10 @@ export class Page implements IPage {
|
|
|
161
196
|
await sendCommand('tabs', { op: 'select', index, ...this._workspaceOpt() });
|
|
162
197
|
}
|
|
163
198
|
|
|
164
|
-
async networkRequests(includeStatic: boolean = false): Promise<
|
|
199
|
+
async networkRequests(includeStatic: boolean = false): Promise<unknown[]> {
|
|
165
200
|
const code = networkRequestsJs(includeStatic);
|
|
166
|
-
|
|
201
|
+
const result = await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
|
|
202
|
+
return Array.isArray(result) ? result : [];
|
|
167
203
|
}
|
|
168
204
|
|
|
169
205
|
/**
|
|
@@ -171,7 +207,7 @@ export class Page implements IPage {
|
|
|
171
207
|
* Would require CDP Runtime.consoleAPICalled event listener.
|
|
172
208
|
* @returns Always returns empty array.
|
|
173
209
|
*/
|
|
174
|
-
async consoleMessages(_level: string = 'info'): Promise<
|
|
210
|
+
async consoleMessages(_level: string = 'info'): Promise<unknown[]> {
|
|
175
211
|
return [];
|
|
176
212
|
}
|
|
177
213
|
|
|
@@ -182,12 +218,7 @@ export class Page implements IPage {
|
|
|
182
218
|
* @param options.fullPage - capture full scrollable page
|
|
183
219
|
* @param options.path - save to file path (returns base64 if omitted)
|
|
184
220
|
*/
|
|
185
|
-
async screenshot(options: {
|
|
186
|
-
format?: 'png' | 'jpeg';
|
|
187
|
-
quality?: number;
|
|
188
|
-
fullPage?: boolean;
|
|
189
|
-
path?: string;
|
|
190
|
-
} = {}): Promise<string> {
|
|
221
|
+
async screenshot(options: ScreenshotOptions = {}): Promise<string> {
|
|
191
222
|
const base64 = await sendCommand('screenshot', {
|
|
192
223
|
...this._workspaceOpt(),
|
|
193
224
|
format: options.format,
|
|
@@ -229,11 +260,11 @@ export class Page implements IPage {
|
|
|
229
260
|
}));
|
|
230
261
|
}
|
|
231
262
|
|
|
232
|
-
async getInterceptedRequests(): Promise<
|
|
263
|
+
async getInterceptedRequests(): Promise<unknown[]> {
|
|
233
264
|
const { generateReadInterceptedJs } = await import('../interceptor.js');
|
|
234
265
|
// Same as installInterceptor: must go through evaluate() for IIFE wrapping
|
|
235
266
|
const result = await this.evaluate(generateReadInterceptedJs('__opencli_xhr'));
|
|
236
|
-
return (result
|
|
267
|
+
return Array.isArray(result) ? result : [];
|
|
237
268
|
}
|
|
238
269
|
}
|
|
239
270
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { parseTsArgsBlock, scanTs, shouldReplaceManifestEntry } from './build-manifest.js';
|
|
3
6
|
|
|
4
7
|
describe('parseTsArgsBlock', () => {
|
|
5
8
|
it('keeps args with nested choices arrays', () => {
|
|
@@ -62,3 +65,68 @@ describe('parseTsArgsBlock', () => {
|
|
|
62
65
|
]);
|
|
63
66
|
});
|
|
64
67
|
});
|
|
68
|
+
|
|
69
|
+
describe('manifest helper rules', () => {
|
|
70
|
+
const tempDirs: string[] = [];
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
for (const dir of tempDirs.splice(0)) {
|
|
74
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('prefers TS adapters over duplicate YAML adapters', () => {
|
|
79
|
+
expect(shouldReplaceManifestEntry(
|
|
80
|
+
{
|
|
81
|
+
site: 'demo',
|
|
82
|
+
name: 'search',
|
|
83
|
+
description: 'yaml',
|
|
84
|
+
strategy: 'public',
|
|
85
|
+
browser: false,
|
|
86
|
+
args: [],
|
|
87
|
+
type: 'yaml',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
site: 'demo',
|
|
91
|
+
name: 'search',
|
|
92
|
+
description: 'ts',
|
|
93
|
+
strategy: 'public',
|
|
94
|
+
browser: false,
|
|
95
|
+
args: [],
|
|
96
|
+
type: 'ts',
|
|
97
|
+
modulePath: 'demo/search.js',
|
|
98
|
+
},
|
|
99
|
+
)).toBe(true);
|
|
100
|
+
|
|
101
|
+
expect(shouldReplaceManifestEntry(
|
|
102
|
+
{
|
|
103
|
+
site: 'demo',
|
|
104
|
+
name: 'search',
|
|
105
|
+
description: 'ts',
|
|
106
|
+
strategy: 'public',
|
|
107
|
+
browser: false,
|
|
108
|
+
args: [],
|
|
109
|
+
type: 'ts',
|
|
110
|
+
modulePath: 'demo/search.js',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
site: 'demo',
|
|
114
|
+
name: 'search',
|
|
115
|
+
description: 'yaml',
|
|
116
|
+
strategy: 'public',
|
|
117
|
+
browser: false,
|
|
118
|
+
args: [],
|
|
119
|
+
type: 'yaml',
|
|
120
|
+
},
|
|
121
|
+
)).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('skips TS files that do not register a cli', () => {
|
|
125
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
|
|
126
|
+
tempDirs.push(dir);
|
|
127
|
+
const file = path.join(dir, 'utils.ts');
|
|
128
|
+
fs.writeFileSync(file, `export function helper() { return 'noop'; }`);
|
|
129
|
+
|
|
130
|
+
expect(scanTs(file, 'demo')).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
});
|
package/src/build-manifest.ts
CHANGED
|
@@ -18,7 +18,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
18
18
|
const CLIS_DIR = path.resolve(__dirname, 'clis');
|
|
19
19
|
const OUTPUT = path.resolve(__dirname, '..', 'dist', 'cli-manifest.json');
|
|
20
20
|
|
|
21
|
-
interface ManifestEntry {
|
|
21
|
+
export interface ManifestEntry {
|
|
22
22
|
site: string;
|
|
23
23
|
name: string;
|
|
24
24
|
description: string;
|
|
@@ -28,19 +28,53 @@ interface ManifestEntry {
|
|
|
28
28
|
args: Array<{
|
|
29
29
|
name: string;
|
|
30
30
|
type?: string;
|
|
31
|
-
default?:
|
|
31
|
+
default?: unknown;
|
|
32
32
|
required?: boolean;
|
|
33
33
|
positional?: boolean;
|
|
34
34
|
help?: string;
|
|
35
35
|
choices?: string[];
|
|
36
36
|
}>;
|
|
37
37
|
columns?: string[];
|
|
38
|
-
pipeline?:
|
|
38
|
+
pipeline?: Record<string, unknown>[];
|
|
39
39
|
timeout?: number;
|
|
40
40
|
/** 'yaml' or 'ts' — determines how executeCommand loads the handler */
|
|
41
41
|
type: 'yaml' | 'ts';
|
|
42
42
|
/** Relative path from clis/ dir, e.g. 'bilibili/hot.yaml' or 'bilibili/search.js' */
|
|
43
43
|
modulePath?: string;
|
|
44
|
+
/** Pre-navigation control — see CliCommand.navigateBefore */
|
|
45
|
+
navigateBefore?: boolean | string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface YamlArgDefinition {
|
|
49
|
+
type?: string;
|
|
50
|
+
default?: unknown;
|
|
51
|
+
required?: boolean;
|
|
52
|
+
positional?: boolean;
|
|
53
|
+
description?: string;
|
|
54
|
+
help?: string;
|
|
55
|
+
choices?: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface YamlCliDefinition {
|
|
59
|
+
site?: string;
|
|
60
|
+
name?: string;
|
|
61
|
+
description?: string;
|
|
62
|
+
domain?: string;
|
|
63
|
+
strategy?: string;
|
|
64
|
+
browser?: boolean;
|
|
65
|
+
args?: Record<string, YamlArgDefinition>;
|
|
66
|
+
columns?: string[];
|
|
67
|
+
pipeline?: Record<string, unknown>[];
|
|
68
|
+
timeout?: number;
|
|
69
|
+
navigateBefore?: boolean | string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
73
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getErrorMessage(error: unknown): string {
|
|
77
|
+
return error instanceof Error ? error.message : String(error);
|
|
44
78
|
}
|
|
45
79
|
|
|
46
80
|
function extractBalancedBlock(
|
|
@@ -127,7 +161,7 @@ export function parseTsArgsBlock(argsBlock: string): ManifestEntry['args'] {
|
|
|
127
161
|
const helpMatch = body.match(/help\s*:\s*['"`]([^'"`]*)['"`]/);
|
|
128
162
|
const positionalMatch = body.match(/positional\s*:\s*(true|false)/);
|
|
129
163
|
|
|
130
|
-
let defaultVal:
|
|
164
|
+
let defaultVal: unknown = undefined;
|
|
131
165
|
if (defaultMatch) {
|
|
132
166
|
const raw = defaultMatch[1].trim();
|
|
133
167
|
if (raw === 'true') defaultVal = true;
|
|
@@ -156,21 +190,23 @@ export function parseTsArgsBlock(argsBlock: string): ManifestEntry['args'] {
|
|
|
156
190
|
function scanYaml(filePath: string, site: string): ManifestEntry | null {
|
|
157
191
|
try {
|
|
158
192
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
159
|
-
const def = yaml.load(raw) as
|
|
160
|
-
if (!def
|
|
193
|
+
const def = yaml.load(raw) as YamlCliDefinition | null;
|
|
194
|
+
if (!isRecord(def)) return null;
|
|
195
|
+
const cliDef = def as YamlCliDefinition;
|
|
161
196
|
|
|
162
|
-
const strategyStr =
|
|
197
|
+
const strategyStr = cliDef.strategy ?? (cliDef.browser === false ? 'public' : 'cookie');
|
|
163
198
|
const strategy = strategyStr.toUpperCase();
|
|
164
|
-
const browser =
|
|
199
|
+
const browser = cliDef.browser ?? (strategy !== 'PUBLIC');
|
|
165
200
|
|
|
166
201
|
const args: ManifestEntry['args'] = [];
|
|
167
|
-
if (
|
|
168
|
-
for (const [argName, argDef] of Object.entries(
|
|
202
|
+
if (cliDef.args && typeof cliDef.args === 'object') {
|
|
203
|
+
for (const [argName, argDef] of Object.entries(cliDef.args)) {
|
|
169
204
|
args.push({
|
|
170
205
|
name: argName,
|
|
171
206
|
type: argDef?.type ?? 'str',
|
|
172
207
|
default: argDef?.default,
|
|
173
208
|
required: argDef?.required ?? false,
|
|
209
|
+
positional: argDef?.positional === true || undefined,
|
|
174
210
|
help: argDef?.description ?? argDef?.help ?? '',
|
|
175
211
|
choices: argDef?.choices,
|
|
176
212
|
});
|
|
@@ -178,25 +214,26 @@ function scanYaml(filePath: string, site: string): ManifestEntry | null {
|
|
|
178
214
|
}
|
|
179
215
|
|
|
180
216
|
return {
|
|
181
|
-
site:
|
|
182
|
-
name:
|
|
183
|
-
description:
|
|
184
|
-
domain:
|
|
217
|
+
site: cliDef.site ?? site,
|
|
218
|
+
name: cliDef.name ?? path.basename(filePath, path.extname(filePath)),
|
|
219
|
+
description: cliDef.description ?? '',
|
|
220
|
+
domain: cliDef.domain,
|
|
185
221
|
strategy: strategy.toLowerCase(),
|
|
186
222
|
browser,
|
|
187
223
|
args,
|
|
188
|
-
columns:
|
|
189
|
-
pipeline:
|
|
190
|
-
timeout:
|
|
224
|
+
columns: cliDef.columns,
|
|
225
|
+
pipeline: cliDef.pipeline,
|
|
226
|
+
timeout: cliDef.timeout,
|
|
191
227
|
type: 'yaml',
|
|
228
|
+
navigateBefore: cliDef.navigateBefore,
|
|
192
229
|
};
|
|
193
|
-
} catch (err
|
|
194
|
-
process.stderr.write(`Warning: failed to parse ${filePath}: ${err
|
|
230
|
+
} catch (err) {
|
|
231
|
+
process.stderr.write(`Warning: failed to parse ${filePath}: ${getErrorMessage(err)}\n`);
|
|
195
232
|
return null;
|
|
196
233
|
}
|
|
197
234
|
}
|
|
198
235
|
|
|
199
|
-
function scanTs(filePath: string, site: string): ManifestEntry | null {
|
|
236
|
+
export function scanTs(filePath: string, site: string): ManifestEntry | null {
|
|
200
237
|
// TS adapters self-register via cli() at import time.
|
|
201
238
|
// We statically parse the source to extract metadata for the manifest stub.
|
|
202
239
|
const baseName = path.basename(filePath, path.extname(filePath));
|
|
@@ -248,16 +285,29 @@ function scanTs(filePath: string, site: string): ManifestEntry | null {
|
|
|
248
285
|
entry.args = parseTsArgsBlock(argsBlock);
|
|
249
286
|
}
|
|
250
287
|
|
|
288
|
+
// Extract navigateBefore: false
|
|
289
|
+
const navMatch = src.match(/navigateBefore\s*:\s*(true|false)/);
|
|
290
|
+
if (navMatch) entry.navigateBefore = navMatch[1] === 'true' ? true : false;
|
|
291
|
+
|
|
251
292
|
return entry;
|
|
252
|
-
} catch (err
|
|
293
|
+
} catch (err) {
|
|
253
294
|
// If parsing fails, log a warning (matching scanYaml behaviour) and skip the entry.
|
|
254
|
-
process.stderr.write(`Warning: failed to scan ${filePath}: ${err
|
|
295
|
+
process.stderr.write(`Warning: failed to scan ${filePath}: ${getErrorMessage(err)}\n`);
|
|
255
296
|
return null;
|
|
256
297
|
}
|
|
257
298
|
}
|
|
258
299
|
|
|
300
|
+
/**
|
|
301
|
+
* When both YAML and TS adapters exist for the same site/name,
|
|
302
|
+
* prefer the TS version (it self-registers and typically has richer logic).
|
|
303
|
+
*/
|
|
304
|
+
export function shouldReplaceManifestEntry(current: ManifestEntry, next: ManifestEntry): boolean {
|
|
305
|
+
if (current.type === next.type) return true;
|
|
306
|
+
return current.type === 'yaml' && next.type === 'ts';
|
|
307
|
+
}
|
|
308
|
+
|
|
259
309
|
export function buildManifest(): ManifestEntry[] {
|
|
260
|
-
const manifest
|
|
310
|
+
const manifest = new Map<string, ManifestEntry>();
|
|
261
311
|
|
|
262
312
|
if (fs.existsSync(CLIS_DIR)) {
|
|
263
313
|
for (const site of fs.readdirSync(CLIS_DIR)) {
|
|
@@ -267,19 +317,37 @@ export function buildManifest(): ManifestEntry[] {
|
|
|
267
317
|
const filePath = path.join(siteDir, file);
|
|
268
318
|
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
|
|
269
319
|
const entry = scanYaml(filePath, site);
|
|
270
|
-
if (entry)
|
|
320
|
+
if (entry) {
|
|
321
|
+
const key = `${entry.site}/${entry.name}`;
|
|
322
|
+
const existing = manifest.get(key);
|
|
323
|
+
if (!existing || shouldReplaceManifestEntry(existing, entry)) {
|
|
324
|
+
if (existing && existing.type !== entry.type) {
|
|
325
|
+
process.stderr.write(`⚠️ Duplicate adapter ${key}: ${existing.type} superseded by ${entry.type}\n`);
|
|
326
|
+
}
|
|
327
|
+
manifest.set(key, entry);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
271
330
|
} else if (
|
|
272
331
|
(file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts') && file !== 'index.ts') ||
|
|
273
332
|
(file.endsWith('.js') && !file.endsWith('.d.js') && !file.endsWith('.test.js') && file !== 'index.js')
|
|
274
333
|
) {
|
|
275
334
|
const entry = scanTs(filePath, site);
|
|
276
|
-
if (entry)
|
|
335
|
+
if (entry) {
|
|
336
|
+
const key = `${entry.site}/${entry.name}`;
|
|
337
|
+
const existing = manifest.get(key);
|
|
338
|
+
if (!existing || shouldReplaceManifestEntry(existing, entry)) {
|
|
339
|
+
if (existing && existing.type !== entry.type) {
|
|
340
|
+
process.stderr.write(`⚠️ Duplicate adapter ${key}: ${existing.type} superseded by ${entry.type}\n`);
|
|
341
|
+
}
|
|
342
|
+
manifest.set(key, entry);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
277
345
|
}
|
|
278
346
|
}
|
|
279
347
|
}
|
|
280
348
|
}
|
|
281
349
|
|
|
282
|
-
return manifest;
|
|
350
|
+
return [...manifest.values()];
|
|
283
351
|
}
|
|
284
352
|
|
|
285
353
|
function main(): void {
|
package/src/cli.ts
CHANGED
|
@@ -140,7 +140,7 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
140
140
|
: undefined;
|
|
141
141
|
const workspace = `explore:${inferHost(url, opts.site)}`;
|
|
142
142
|
const result = await exploreUrl(url, {
|
|
143
|
-
BrowserFactory: getBrowserFactory()
|
|
143
|
+
BrowserFactory: getBrowserFactory(),
|
|
144
144
|
site: opts.site,
|
|
145
145
|
goal: opts.goal,
|
|
146
146
|
waitSeconds: parseFloat(opts.wait),
|
|
@@ -172,7 +172,7 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
172
172
|
const workspace = `generate:${inferHost(url, opts.site)}`;
|
|
173
173
|
const r = await generateCliFromUrl({
|
|
174
174
|
url,
|
|
175
|
-
BrowserFactory: getBrowserFactory()
|
|
175
|
+
BrowserFactory: getBrowserFactory(),
|
|
176
176
|
builtinClis: BUILTIN_CLIS,
|
|
177
177
|
userClis: USER_CLIS,
|
|
178
178
|
goal: opts.goal,
|
|
@@ -231,6 +231,75 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
231
231
|
printCompletionScript(shell);
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
+
// ── Plugin management ──────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
const pluginCmd = program.command('plugin').description('Manage opencli plugins');
|
|
237
|
+
|
|
238
|
+
pluginCmd
|
|
239
|
+
.command('install')
|
|
240
|
+
.description('Install a plugin from GitHub')
|
|
241
|
+
.argument('<source>', 'Plugin source (e.g. github:user/repo)')
|
|
242
|
+
.action(async (source: string) => {
|
|
243
|
+
const { installPlugin } = await import('./plugin.js');
|
|
244
|
+
try {
|
|
245
|
+
const name = installPlugin(source);
|
|
246
|
+
console.log(chalk.green(`✅ Plugin "${name}" installed successfully.`));
|
|
247
|
+
console.log(chalk.dim(` Restart opencli to use the new commands.`));
|
|
248
|
+
} catch (err: any) {
|
|
249
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
250
|
+
process.exitCode = 1;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
pluginCmd
|
|
255
|
+
.command('uninstall')
|
|
256
|
+
.description('Uninstall a plugin')
|
|
257
|
+
.argument('<name>', 'Plugin name')
|
|
258
|
+
.action(async (name: string) => {
|
|
259
|
+
const { uninstallPlugin } = await import('./plugin.js');
|
|
260
|
+
try {
|
|
261
|
+
uninstallPlugin(name);
|
|
262
|
+
console.log(chalk.green(`✅ Plugin "${name}" uninstalled.`));
|
|
263
|
+
} catch (err: any) {
|
|
264
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
265
|
+
process.exitCode = 1;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
pluginCmd
|
|
270
|
+
.command('list')
|
|
271
|
+
.description('List installed plugins')
|
|
272
|
+
.option('-f, --format <fmt>', 'Output format: table, json', 'table')
|
|
273
|
+
.action(async (opts) => {
|
|
274
|
+
const { listPlugins } = await import('./plugin.js');
|
|
275
|
+
const plugins = listPlugins();
|
|
276
|
+
if (plugins.length === 0) {
|
|
277
|
+
console.log(chalk.dim(' No plugins installed.'));
|
|
278
|
+
console.log(chalk.dim(` Install one with: opencli plugin install github:user/repo`));
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (opts.format === 'json') {
|
|
282
|
+
renderOutput(plugins, {
|
|
283
|
+
fmt: 'json',
|
|
284
|
+
columns: ['name', 'commands', 'source'],
|
|
285
|
+
title: 'opencli/plugins',
|
|
286
|
+
source: 'opencli plugin list',
|
|
287
|
+
});
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
console.log();
|
|
291
|
+
console.log(chalk.bold(' Installed plugins'));
|
|
292
|
+
console.log();
|
|
293
|
+
for (const p of plugins) {
|
|
294
|
+
const cmds = p.commands.length > 0 ? chalk.dim(` (${p.commands.join(', ')})`) : '';
|
|
295
|
+
const src = p.source ? chalk.dim(` ← ${p.source}`) : '';
|
|
296
|
+
console.log(` ${chalk.cyan(p.name)}${cmds}${src}`);
|
|
297
|
+
}
|
|
298
|
+
console.log();
|
|
299
|
+
console.log(chalk.dim(` ${plugins.length} plugin(s) installed`));
|
|
300
|
+
console.log();
|
|
301
|
+
});
|
|
302
|
+
|
|
234
303
|
// ── External CLIs ─────────────────────────────────────────────────────────
|
|
235
304
|
|
|
236
305
|
const externalClis = loadExternalClis();
|
|
@@ -12,7 +12,7 @@ cli({
|
|
|
12
12
|
domain: 'www.barchart.com',
|
|
13
13
|
strategy: Strategy.COOKIE,
|
|
14
14
|
args: [
|
|
15
|
-
{ name: 'symbol', required: true, help: 'Stock ticker (e.g. AAPL)' },
|
|
15
|
+
{ name: 'symbol', required: true, positional: true, help: 'Stock ticker (e.g. AAPL)' },
|
|
16
16
|
{ name: 'expiration', type: 'str', help: 'Expiration date (YYYY-MM-DD). Defaults to the nearest available expiration.' },
|
|
17
17
|
{ name: 'limit', type: 'int', default: 10, help: 'Number of near-the-money strikes per type' },
|
|
18
18
|
],
|
|
@@ -11,7 +11,7 @@ cli({
|
|
|
11
11
|
domain: 'www.barchart.com',
|
|
12
12
|
strategy: Strategy.COOKIE,
|
|
13
13
|
args: [
|
|
14
|
-
{ name: 'symbol', required: true, help: 'Stock ticker (e.g. AAPL)' },
|
|
14
|
+
{ name: 'symbol', required: true, positional: true, help: 'Stock ticker (e.g. AAPL)' },
|
|
15
15
|
{ name: 'type', type: 'str', default: 'Call', help: 'Option type: Call or Put', choices: ['Call', 'Put'] },
|
|
16
16
|
{ name: 'limit', type: 'int', default: 20, help: 'Max number of strikes to return' },
|
|
17
17
|
],
|
|
@@ -11,7 +11,7 @@ cli({
|
|
|
11
11
|
domain: 'www.barchart.com',
|
|
12
12
|
strategy: Strategy.COOKIE,
|
|
13
13
|
args: [
|
|
14
|
-
{ name: 'symbol', required: true, help: 'Stock ticker (e.g. AAPL, MSFT, TSLA)' },
|
|
14
|
+
{ name: 'symbol', required: true, positional: true, help: 'Stock ticker (e.g. AAPL, MSFT, TSLA)' },
|
|
15
15
|
],
|
|
16
16
|
columns: [
|
|
17
17
|
'symbol', 'name', 'price', 'change', 'changePct',
|
|
@@ -28,7 +28,7 @@ cli({
|
|
|
28
28
|
domain: 'www.bilibili.com',
|
|
29
29
|
strategy: Strategy.COOKIE,
|
|
30
30
|
args: [
|
|
31
|
-
{ name: 'bvid', required: true, help: 'Video BV ID (e.g., BV1xxx)' },
|
|
31
|
+
{ name: 'bvid', required: true, positional: true, help: 'Video BV ID (e.g., BV1xxx)' },
|
|
32
32
|
{ name: 'output', default: './bilibili-downloads', help: 'Output directory' },
|
|
33
33
|
{ name: 'quality', default: 'best', help: 'Video quality (best, 1080p, 720p, 480p)' },
|
|
34
34
|
],
|
|
@@ -8,7 +8,7 @@ cli({
|
|
|
8
8
|
description: '获取 Bilibili 用户的关注列表',
|
|
9
9
|
strategy: Strategy.COOKIE,
|
|
10
10
|
args: [
|
|
11
|
-
{ name: 'uid', required: false, help: '目标用户 ID(默认为当前登录用户)' },
|
|
11
|
+
{ name: 'uid', positional: true, required: false, help: '目标用户 ID(默认为当前登录用户)' },
|
|
12
12
|
{ name: 'page', type: 'int', required: false, default: 1, help: '页码' },
|
|
13
13
|
{ name: 'limit', type: 'int', required: false, default: 50, help: '每页数量 (最大 50)' },
|
|
14
14
|
],
|
|
@@ -8,7 +8,7 @@ cli({
|
|
|
8
8
|
description: '获取 Bilibili 视频的字幕',
|
|
9
9
|
strategy: Strategy.COOKIE,
|
|
10
10
|
args: [
|
|
11
|
-
{ name: 'bvid', required: true },
|
|
11
|
+
{ name: 'bvid', required: true, positional: true },
|
|
12
12
|
{ name: 'lang', required: false, help: '字幕语言代码 (如 zh-CN, en-US, ai-zh),默认取第一个' },
|
|
13
13
|
],
|
|
14
14
|
columns: ['index', 'from', 'to', 'content'],
|
|
@@ -8,7 +8,7 @@ cli({
|
|
|
8
8
|
domain: 'www.bilibili.com',
|
|
9
9
|
strategy: Strategy.COOKIE,
|
|
10
10
|
args: [
|
|
11
|
-
{ name: 'uid', required: true, help: 'User UID or username' },
|
|
11
|
+
{ name: 'uid', required: true, positional: true, help: 'User UID or username' },
|
|
12
12
|
{ name: 'limit', type: 'int', default: 20, help: 'Number of results' },
|
|
13
13
|
{ name: 'order', default: 'pubdate', help: 'Sort: pubdate, click, stow' },
|
|
14
14
|
{ name: 'page', type: 'int', default: 1, help: 'Page number' },
|