@jackwener/opencli 1.1.0 → 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/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
- package/.github/pull_request_template.md +7 -0
- package/.github/workflows/doc-check.yml +36 -0
- package/.github/workflows/docs.yml +7 -42
- package/CHANGELOG.md +23 -0
- package/CLI-EXPLORER.md +9 -8
- package/CONTRIBUTING.md +39 -1
- package/README.md +33 -19
- package/README.zh-CN.md +64 -27
- package/SKILL.md +102 -33
- package/dist/browser/cdp.d.ts +4 -4
- package/dist/browser/cdp.js +45 -17
- 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 +18 -25
- package/dist/browser/page.js +44 -5
- package/dist/build-manifest.d.ts +11 -4
- package/dist/build-manifest.js +79 -34
- package/dist/build-manifest.test.js +58 -2
- package/dist/cli-manifest.json +4273 -1771
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +255 -162
- package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
- package/dist/clis/apple-podcasts/commands.test.js +76 -0
- package/dist/clis/apple-podcasts/search.js +2 -2
- package/dist/clis/apple-podcasts/top.js +9 -2
- package/dist/clis/arxiv/search.js +1 -1
- 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/dynamic.js +1 -1
- package/dist/clis/bilibili/favorite.js +1 -1
- package/dist/clis/bilibili/feed.js +1 -1
- package/dist/clis/bilibili/following.js +2 -2
- package/dist/clis/bilibili/history.js +1 -1
- package/dist/clis/bilibili/me.js +1 -1
- package/dist/clis/bilibili/ranking.js +1 -1
- package/dist/clis/bilibili/search.js +3 -3
- package/dist/clis/bilibili/subtitle.js +2 -2
- package/dist/clis/bilibili/user-videos.js +2 -2
- package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
- package/dist/clis/bloomberg/businessweek.js +17 -0
- package/dist/clis/bloomberg/economics.js +17 -0
- package/dist/clis/bloomberg/feeds.d.ts +1 -0
- package/dist/clis/bloomberg/feeds.js +15 -0
- package/dist/clis/bloomberg/industries.d.ts +1 -0
- package/dist/clis/bloomberg/industries.js +17 -0
- package/dist/clis/bloomberg/main.d.ts +1 -0
- package/dist/clis/bloomberg/main.js +17 -0
- package/dist/clis/bloomberg/markets.d.ts +1 -0
- package/dist/clis/bloomberg/markets.js +17 -0
- package/dist/clis/bloomberg/news.d.ts +1 -0
- package/dist/clis/bloomberg/news.js +105 -0
- package/dist/clis/bloomberg/opinions.d.ts +1 -0
- package/dist/clis/bloomberg/opinions.js +17 -0
- package/dist/clis/bloomberg/politics.d.ts +1 -0
- package/dist/clis/bloomberg/politics.js +17 -0
- package/dist/clis/bloomberg/tech.d.ts +1 -0
- package/dist/clis/bloomberg/tech.js +17 -0
- package/dist/clis/bloomberg/utils.d.ts +34 -0
- package/dist/clis/bloomberg/utils.js +364 -0
- package/dist/clis/bloomberg/utils.test.d.ts +1 -0
- package/dist/clis/bloomberg/utils.test.js +129 -0
- package/dist/clis/boss/batchgreet.js +12 -99
- package/dist/clis/boss/chatlist.js +9 -26
- 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 +8 -50
- package/dist/clis/boss/exchange.js +13 -79
- package/dist/clis/boss/greet.js +20 -147
- 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 +13 -61
- package/dist/clis/boss/send.js +18 -152
- package/dist/clis/boss/stats.js +20 -71
- package/dist/clis/chaoxing/assignments.js +1 -1
- package/dist/clis/chaoxing/exams.js +1 -1
- package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
- package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
- package/dist/clis/chaoxing/utils.test.d.ts +1 -0
- package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
- package/dist/clis/chatgpt/read.js +1 -1
- package/dist/clis/chatwise/export.js +1 -1
- package/dist/clis/chatwise/model.js +2 -2
- package/dist/clis/chatwise/screenshot.js +1 -1
- package/dist/clis/codex/export.js +1 -1
- package/dist/clis/codex/model.js +2 -2
- package/dist/clis/codex/screenshot.js +1 -1
- package/dist/clis/coupang/add-to-cart.js +3 -4
- package/dist/clis/coupang/search.js +2 -4
- package/dist/clis/coupang/utils.test.d.ts +1 -0
- package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
- package/dist/clis/ctrip/search.js +1 -1
- package/dist/clis/cursor/export.js +1 -1
- package/dist/clis/cursor/model.js +2 -2
- package/dist/clis/cursor/screenshot.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/comment.js +2 -3
- package/dist/clis/jike/create.js +1 -2
- package/dist/clis/jike/feed.js +0 -1
- package/dist/clis/jike/like.js +1 -2
- package/dist/clis/jike/notifications.js +0 -1
- package/dist/clis/jike/post.yaml +1 -0
- package/dist/clis/jike/repost.js +2 -3
- package/dist/clis/jike/search.js +2 -3
- package/dist/clis/jike/topic.yaml +1 -0
- package/dist/clis/jike/user.yaml +1 -0
- package/dist/clis/jimeng/generate.yaml +1 -0
- package/dist/clis/jimeng/history.yaml +0 -1
- package/dist/clis/linkedin/search.js +7 -7
- package/dist/clis/linux-do/category.yaml +2 -0
- package/dist/clis/linux-do/search.yaml +4 -3
- package/dist/clis/linux-do/topic.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/notion/export.js +1 -1
- package/dist/clis/reddit/comment.js +3 -4
- package/dist/clis/reddit/read.js +4 -5
- package/dist/clis/reddit/save.js +2 -3
- package/dist/clis/reddit/saved.js +0 -1
- package/dist/clis/reddit/search.yaml +1 -0
- package/dist/clis/reddit/subreddit.yaml +1 -0
- package/dist/clis/reddit/subscribe.js +1 -2
- package/dist/clis/reddit/upvote.js +2 -3
- package/dist/clis/reddit/upvoted.js +0 -1
- package/dist/clis/reddit/user-comments.yaml +1 -0
- package/dist/clis/reddit/user-posts.yaml +1 -0
- package/dist/clis/reddit/user.yaml +1 -0
- package/dist/clis/reuters/search.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/smzdm/search.js +2 -3
- package/dist/clis/stackoverflow/search.yaml +1 -0
- package/dist/clis/steam/top-sellers.yaml +29 -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/accept.js +2 -2
- package/dist/clis/twitter/article.js +2 -2
- package/dist/clis/twitter/block.d.ts +1 -0
- package/dist/clis/twitter/block.js +88 -0
- package/dist/clis/twitter/delete.js +1 -1
- 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/hide-reply.d.ts +1 -0
- package/dist/clis/twitter/hide-reply.js +66 -0
- package/dist/clis/twitter/like.js +1 -1
- package/dist/clis/twitter/post.js +1 -1
- package/dist/clis/twitter/reply-dm.js +1 -1
- package/dist/clis/twitter/reply.js +2 -2
- package/dist/clis/twitter/search.js +1 -1
- package/dist/clis/twitter/thread.js +2 -2
- 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/twitter/trending.d.ts +1 -0
- package/dist/clis/twitter/trending.js +91 -0
- package/dist/clis/twitter/unblock.d.ts +1 -0
- package/dist/clis/twitter/unblock.js +71 -0
- package/dist/clis/v2ex/topic.yaml +1 -0
- package/dist/clis/weibo/hot.js +0 -1
- package/dist/clis/weread/book.js +1 -1
- package/dist/clis/weread/highlights.js +1 -1
- package/dist/clis/weread/notes.js +1 -1
- package/dist/clis/weread/search.js +1 -1
- package/dist/clis/wikipedia/random.d.ts +1 -0
- package/dist/clis/wikipedia/random.js +19 -0
- package/dist/clis/wikipedia/search.js +4 -4
- 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.d.ts +15 -0
- package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +82 -33
- package/dist/clis/xiaohongshu/creator-notes.js +35 -5
- package/dist/clis/xiaohongshu/creator-notes.test.js +37 -6
- package/dist/clis/xiaohongshu/creator-profile.js +0 -1
- package/dist/clis/xiaohongshu/creator-stats.js +0 -1
- package/dist/clis/xiaohongshu/download.js +2 -3
- package/dist/clis/xiaohongshu/feed.yaml +0 -1
- package/dist/clis/xiaohongshu/notifications.yaml +0 -1
- package/dist/clis/xiaohongshu/search.js +2 -2
- package/dist/clis/xiaohongshu/user.js +1 -2
- 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 -2
- package/dist/clis/youtube/search.js +1 -1
- package/dist/clis/youtube/transcript.js +1 -1
- package/dist/clis/youtube/video.js +1 -1
- package/dist/clis/zhihu/download.js +1 -2
- package/dist/clis/zhihu/question.js +1 -1
- package/dist/clis/zhihu/search.yaml +4 -3
- package/dist/commanderAdapter.d.ts +21 -0
- package/dist/commanderAdapter.js +117 -0
- package/dist/{engine.d.ts → discovery.d.ts} +6 -4
- package/dist/{engine.js → discovery.js} +93 -104
- package/dist/doctor.js +3 -1
- package/dist/doctor.test.js +46 -2
- package/dist/download/index.d.ts +2 -6
- package/dist/download/index.js +19 -46
- package/dist/engine.test.d.ts +0 -3
- package/dist/engine.test.js +80 -11
- package/dist/execution.d.ts +24 -0
- package/dist/execution.js +153 -0
- package/dist/explore.d.ts +76 -3
- package/dist/explore.js +132 -111
- package/dist/external-clis.yaml +48 -0
- package/dist/external.d.ts +7 -2
- package/dist/external.js +11 -14
- 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 +21 -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 +13 -0
- package/dist/registry.js +8 -1
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +8 -0
- package/dist/serialization.d.ts +34 -0
- package/dist/serialization.js +63 -0
- package/dist/synthesize.d.ts +94 -4
- package/dist/synthesize.js +5 -4
- package/dist/types.d.ts +43 -27
- package/dist/validate.js +8 -2
- package/docs/.vitepress/config.mts +20 -7
- package/docs/adapters/browser/arxiv.md +27 -0
- package/docs/adapters/browser/barchart.md +33 -0
- package/docs/adapters/browser/bilibili.md +9 -0
- package/docs/adapters/browser/bloomberg.md +70 -0
- package/docs/adapters/browser/chaoxing.md +39 -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 +53 -0
- package/docs/adapters/browser/hf.md +42 -0
- package/docs/adapters/browser/instagram.md +46 -0
- package/docs/adapters/browser/jike.md +45 -0
- package/docs/adapters/browser/jimeng.md +39 -0
- package/docs/adapters/browser/linux-do.md +45 -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/sinafinance.md +35 -0
- package/docs/adapters/browser/stackoverflow.md +35 -0
- package/docs/adapters/browser/steam.md +26 -0
- package/docs/adapters/browser/substack.md +38 -0
- package/docs/adapters/browser/tiktok.md +68 -0
- package/docs/adapters/browser/twitter.md +3 -0
- package/docs/adapters/browser/weread.md +48 -0
- package/docs/adapters/browser/wikipedia.md +39 -0
- package/docs/adapters/browser/xiaohongshu.md +5 -1
- 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 +5 -4
- 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 +14 -4
- package/docs/advanced/download.md +4 -4
- package/docs/developer/architecture.md +17 -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/check-doc-coverage.sh +69 -0
- package/scripts/clean-dist.cjs +13 -0
- package/scripts/copy-yaml.cjs +7 -0
- package/src/browser/cdp.ts +77 -32
- 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 +57 -20
- package/src/build-manifest.test.ts +70 -2
- package/src/build-manifest.ts +114 -40
- package/src/cli.ts +287 -139
- package/src/clis/apple-podcasts/commands.test.ts +95 -0
- package/src/clis/apple-podcasts/search.ts +2 -2
- package/src/clis/apple-podcasts/top.ts +12 -2
- package/src/clis/arxiv/search.ts +1 -1
- 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/dynamic.ts +1 -1
- package/src/clis/bilibili/favorite.ts +1 -1
- package/src/clis/bilibili/feed.ts +1 -1
- package/src/clis/bilibili/following.ts +2 -2
- package/src/clis/bilibili/history.ts +1 -1
- package/src/clis/bilibili/me.ts +1 -1
- package/src/clis/bilibili/ranking.ts +1 -1
- package/src/clis/bilibili/search.ts +3 -3
- package/src/clis/bilibili/subtitle.ts +2 -2
- package/src/clis/bilibili/user-videos.ts +2 -2
- package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
- package/src/clis/bloomberg/businessweek.ts +18 -0
- package/src/clis/bloomberg/economics.ts +18 -0
- package/src/clis/bloomberg/feeds.ts +16 -0
- package/src/clis/bloomberg/industries.ts +18 -0
- package/src/clis/bloomberg/main.ts +18 -0
- package/src/clis/bloomberg/markets.ts +18 -0
- package/src/clis/bloomberg/news.ts +136 -0
- package/src/clis/bloomberg/opinions.ts +18 -0
- package/src/clis/bloomberg/politics.ts +18 -0
- package/src/clis/bloomberg/tech.ts +18 -0
- package/src/clis/bloomberg/utils.test.ts +135 -0
- package/src/clis/bloomberg/utils.ts +429 -0
- package/src/clis/boss/batchgreet.ts +16 -108
- package/src/clis/boss/chatlist.ts +13 -27
- package/src/clis/boss/chatmsg.ts +16 -40
- package/src/clis/boss/common.ts +287 -0
- package/src/clis/boss/detail.ts +9 -55
- package/src/clis/boss/exchange.ts +15 -89
- package/src/clis/boss/greet.ts +25 -162
- 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 +14 -67
- package/src/clis/boss/send.ts +22 -162
- package/src/clis/boss/stats.ts +21 -76
- package/src/clis/chaoxing/assignments.ts +1 -1
- package/src/clis/chaoxing/exams.ts +1 -1
- package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
- package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
- package/src/clis/chatgpt/read.ts +1 -1
- package/src/clis/chatwise/export.ts +1 -1
- package/src/clis/chatwise/model.ts +2 -2
- package/src/clis/chatwise/screenshot.ts +1 -1
- package/src/clis/codex/export.ts +1 -1
- package/src/clis/codex/model.ts +2 -2
- package/src/clis/codex/screenshot.ts +1 -1
- package/src/clis/coupang/add-to-cart.ts +3 -4
- package/src/clis/coupang/search.ts +2 -4
- package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
- package/src/clis/ctrip/search.ts +1 -1
- package/src/clis/cursor/export.ts +1 -1
- package/src/clis/cursor/model.ts +2 -2
- package/src/clis/cursor/screenshot.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/comment.ts +2 -3
- package/src/clis/jike/create.ts +1 -2
- package/src/clis/jike/feed.ts +0 -1
- package/src/clis/jike/like.ts +1 -2
- package/src/clis/jike/notifications.ts +0 -1
- package/src/clis/jike/post.yaml +1 -0
- package/src/clis/jike/repost.ts +2 -3
- package/src/clis/jike/search.ts +2 -3
- package/src/clis/jike/topic.yaml +1 -0
- package/src/clis/jike/user.yaml +1 -0
- package/src/clis/jimeng/generate.yaml +1 -0
- package/src/clis/jimeng/history.yaml +0 -1
- package/src/clis/linkedin/search.ts +7 -7
- package/src/clis/linux-do/category.yaml +2 -0
- package/src/clis/linux-do/search.yaml +4 -3
- package/src/clis/linux-do/topic.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/notion/export.ts +1 -1
- package/src/clis/reddit/comment.ts +3 -4
- package/src/clis/reddit/read.ts +4 -5
- package/src/clis/reddit/save.ts +2 -3
- package/src/clis/reddit/saved.ts +0 -1
- package/src/clis/reddit/search.yaml +1 -0
- package/src/clis/reddit/subreddit.yaml +1 -0
- package/src/clis/reddit/subscribe.ts +1 -2
- package/src/clis/reddit/upvote.ts +2 -3
- package/src/clis/reddit/upvoted.ts +0 -1
- package/src/clis/reddit/user-comments.yaml +1 -0
- package/src/clis/reddit/user-posts.yaml +1 -0
- package/src/clis/reddit/user.yaml +1 -0
- package/src/clis/reuters/search.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/smzdm/search.ts +2 -3
- package/src/clis/stackoverflow/search.yaml +1 -0
- package/src/clis/steam/top-sellers.yaml +29 -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/accept.ts +2 -2
- package/src/clis/twitter/article.ts +2 -2
- package/src/clis/twitter/block.ts +92 -0
- package/src/clis/twitter/delete.ts +1 -1
- 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/hide-reply.ts +70 -0
- package/src/clis/twitter/like.ts +1 -1
- package/src/clis/twitter/post.ts +1 -1
- package/src/clis/twitter/reply-dm.ts +1 -1
- package/src/clis/twitter/reply.ts +2 -2
- package/src/clis/twitter/search.ts +1 -1
- package/src/clis/twitter/thread.ts +2 -2
- package/src/clis/twitter/timeline.test.ts +109 -0
- package/src/clis/twitter/timeline.ts +59 -19
- package/src/clis/twitter/trending.ts +113 -0
- package/src/clis/twitter/unblock.ts +75 -0
- package/src/clis/v2ex/topic.yaml +1 -0
- package/src/clis/weibo/hot.ts +0 -1
- package/src/clis/weread/book.ts +1 -1
- package/src/clis/weread/highlights.ts +1 -1
- package/src/clis/weread/notes.ts +1 -1
- package/src/clis/weread/search.ts +1 -1
- package/src/clis/wikipedia/random.ts +19 -0
- package/src/clis/wikipedia/search.ts +11 -5
- 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 +84 -33
- package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
- package/src/clis/xiaohongshu/creator-notes.test.ts +41 -6
- package/src/clis/xiaohongshu/creator-notes.ts +44 -5
- package/src/clis/xiaohongshu/creator-profile.ts +0 -1
- package/src/clis/xiaohongshu/creator-stats.ts +0 -1
- package/src/clis/xiaohongshu/download.ts +2 -3
- package/src/clis/xiaohongshu/feed.yaml +0 -1
- package/src/clis/xiaohongshu/notifications.yaml +0 -1
- package/src/clis/xiaohongshu/search.ts +2 -2
- package/src/clis/xiaohongshu/user.ts +1 -2
- 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 -2
- package/src/clis/youtube/search.ts +1 -1
- package/src/clis/youtube/transcript.ts +1 -1
- package/src/clis/youtube/video.ts +1 -1
- package/src/clis/zhihu/download.ts +1 -2
- package/src/clis/zhihu/question.ts +1 -1
- package/src/clis/zhihu/search.yaml +4 -3
- package/src/commanderAdapter.ts +120 -0
- package/src/discovery.ts +277 -0
- package/src/doctor.test.ts +59 -2
- package/src/doctor.ts +4 -2
- package/src/download/index.ts +21 -54
- package/src/engine.test.ts +85 -11
- package/src/execution.ts +164 -0
- package/src/explore.ts +211 -117
- package/src/external-clis.yaml +9 -0
- package/src/external.ts +15 -12
- 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 +29 -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 +24 -1
- package/src/runtime.ts +9 -0
- package/src/serialization.ts +79 -0
- package/src/synthesize.ts +102 -21
- package/src/types.ts +45 -13
- package/src/validate.ts +19 -4
- package/tests/e2e/browser-public.test.ts +36 -0
- package/tests/e2e/public-commands.test.ts +119 -1
- 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/twitter/trending.yaml +0 -46
- 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/docs/public/CNAME +0 -1
- 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/twitter/trending.yaml +0 -46
- 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/engine.ts +0 -274
- package/src/pipeline.ts +0 -8
- /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
- /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
- /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
- /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
- /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
- /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
|
@@ -8,14 +8,27 @@
|
|
|
8
8
|
* 2. FALLBACK (filesystem scan): Traditional runtime discovery for development.
|
|
9
9
|
*/
|
|
10
10
|
import * as fs from 'node:fs';
|
|
11
|
+
import * as os from 'node:os';
|
|
11
12
|
import * as path from 'node:path';
|
|
13
|
+
import { pathToFileURL } from 'node:url';
|
|
12
14
|
import yaml from 'js-yaml';
|
|
13
15
|
import { Strategy, registerCommand } from './registry.js';
|
|
14
|
-
import { executePipeline } from './pipeline.js';
|
|
15
16
|
import { log } from './logger.js';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
17
|
+
/** Plugins directory: ~/.opencli/plugins/ */
|
|
18
|
+
export const PLUGINS_DIR = path.join(os.homedir(), '.opencli', 'plugins');
|
|
19
|
+
const CLI_MODULE_PATTERN = /\bcli\s*\(/;
|
|
20
|
+
function getErrorMessage(error) {
|
|
21
|
+
return error instanceof Error ? error.message : String(error);
|
|
22
|
+
}
|
|
23
|
+
function parseStrategy(rawStrategy, fallback = Strategy.COOKIE) {
|
|
24
|
+
if (!rawStrategy)
|
|
25
|
+
return fallback;
|
|
26
|
+
const key = rawStrategy.toUpperCase();
|
|
27
|
+
return Strategy[key] ?? fallback;
|
|
28
|
+
}
|
|
29
|
+
function isRecord(value) {
|
|
30
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
31
|
+
}
|
|
19
32
|
/**
|
|
20
33
|
* Discover and register CLI commands.
|
|
21
34
|
* Uses pre-compiled manifest when available for instant startup.
|
|
@@ -47,7 +60,7 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
47
60
|
for (const entry of manifest) {
|
|
48
61
|
if (entry.type === 'yaml') {
|
|
49
62
|
// YAML pipelines fully inlined in manifest — register directly
|
|
50
|
-
const strategy =
|
|
63
|
+
const strategy = parseStrategy(entry.strategy);
|
|
51
64
|
const cmd = {
|
|
52
65
|
site: entry.site,
|
|
53
66
|
name: entry.name,
|
|
@@ -60,13 +73,14 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
60
73
|
pipeline: entry.pipeline,
|
|
61
74
|
timeoutSeconds: entry.timeout,
|
|
62
75
|
source: `manifest:${entry.site}/${entry.name}`,
|
|
76
|
+
navigateBefore: entry.navigateBefore,
|
|
63
77
|
};
|
|
64
78
|
registerCommand(cmd);
|
|
65
79
|
}
|
|
66
80
|
else if (entry.type === 'ts' && entry.modulePath) {
|
|
67
81
|
// TS adapters: register a lightweight stub.
|
|
68
82
|
// The actual module is loaded lazily on first executeCommand().
|
|
69
|
-
const strategy =
|
|
83
|
+
const strategy = parseStrategy(entry.strategy ?? 'cookie');
|
|
70
84
|
const modulePath = path.resolve(clisDir, entry.modulePath);
|
|
71
85
|
const cmd = {
|
|
72
86
|
site: entry.site,
|
|
@@ -79,6 +93,7 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
79
93
|
columns: entry.columns,
|
|
80
94
|
timeoutSeconds: entry.timeout,
|
|
81
95
|
source: modulePath,
|
|
96
|
+
navigateBefore: entry.navigateBefore,
|
|
82
97
|
_lazy: true,
|
|
83
98
|
_modulePath: modulePath,
|
|
84
99
|
};
|
|
@@ -87,7 +102,7 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
87
102
|
}
|
|
88
103
|
}
|
|
89
104
|
catch (err) {
|
|
90
|
-
log.warn(`Failed to load manifest ${manifestPath}: ${err
|
|
105
|
+
log.warn(`Failed to load manifest ${manifestPath}: ${getErrorMessage(err)}`);
|
|
91
106
|
}
|
|
92
107
|
}
|
|
93
108
|
/**
|
|
@@ -115,8 +130,10 @@ async function discoverClisFromFs(dir) {
|
|
|
115
130
|
}
|
|
116
131
|
else if ((file.endsWith('.js') && !file.endsWith('.d.js')) ||
|
|
117
132
|
(file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts'))) {
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
if (!(await isCliModule(filePath)))
|
|
134
|
+
continue;
|
|
135
|
+
promises.push(import(pathToFileURL(filePath).href).catch((err) => {
|
|
136
|
+
log.warn(`Failed to load module ${filePath}: ${getErrorMessage(err)}`);
|
|
120
137
|
}));
|
|
121
138
|
}
|
|
122
139
|
}
|
|
@@ -127,21 +144,23 @@ async function registerYamlCli(filePath, defaultSite) {
|
|
|
127
144
|
try {
|
|
128
145
|
const raw = await fs.promises.readFile(filePath, 'utf-8');
|
|
129
146
|
const def = yaml.load(raw);
|
|
130
|
-
if (!def
|
|
147
|
+
if (!isRecord(def))
|
|
131
148
|
return;
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const
|
|
149
|
+
const cliDef = def;
|
|
150
|
+
const site = cliDef.site ?? defaultSite;
|
|
151
|
+
const name = cliDef.name ?? path.basename(filePath, path.extname(filePath));
|
|
152
|
+
const strategyStr = cliDef.strategy ?? (cliDef.browser === false ? 'public' : 'cookie');
|
|
153
|
+
const strategy = parseStrategy(strategyStr);
|
|
154
|
+
const browser = cliDef.browser ?? (strategy !== Strategy.PUBLIC);
|
|
137
155
|
const args = [];
|
|
138
|
-
if (
|
|
139
|
-
for (const [argName, argDef] of Object.entries(
|
|
156
|
+
if (cliDef.args && typeof cliDef.args === 'object') {
|
|
157
|
+
for (const [argName, argDef] of Object.entries(cliDef.args)) {
|
|
140
158
|
args.push({
|
|
141
159
|
name: argName,
|
|
142
160
|
type: argDef?.type ?? 'str',
|
|
143
161
|
default: argDef?.default,
|
|
144
162
|
required: argDef?.required ?? false,
|
|
163
|
+
positional: argDef?.positional ?? false,
|
|
145
164
|
help: argDef?.description ?? argDef?.help ?? '',
|
|
146
165
|
choices: argDef?.choices,
|
|
147
166
|
});
|
|
@@ -150,113 +169,83 @@ async function registerYamlCli(filePath, defaultSite) {
|
|
|
150
169
|
const cmd = {
|
|
151
170
|
site,
|
|
152
171
|
name,
|
|
153
|
-
description:
|
|
154
|
-
domain:
|
|
172
|
+
description: cliDef.description ?? '',
|
|
173
|
+
domain: cliDef.domain,
|
|
155
174
|
strategy,
|
|
156
175
|
browser,
|
|
157
176
|
args,
|
|
158
|
-
columns:
|
|
159
|
-
pipeline:
|
|
160
|
-
timeoutSeconds:
|
|
177
|
+
columns: cliDef.columns,
|
|
178
|
+
pipeline: cliDef.pipeline,
|
|
179
|
+
timeoutSeconds: cliDef.timeout,
|
|
161
180
|
source: filePath,
|
|
181
|
+
navigateBefore: cliDef.navigateBefore,
|
|
162
182
|
};
|
|
163
183
|
registerCommand(cmd);
|
|
164
184
|
}
|
|
165
185
|
catch (err) {
|
|
166
|
-
log.warn(`Failed to load ${filePath}: ${err
|
|
186
|
+
log.warn(`Failed to load ${filePath}: ${getErrorMessage(err)}`);
|
|
167
187
|
}
|
|
168
188
|
}
|
|
169
189
|
/**
|
|
170
|
-
*
|
|
190
|
+
* Discover and register plugins from ~/.opencli/plugins/.
|
|
191
|
+
* Each subdirectory is treated as a plugin (site = directory name).
|
|
192
|
+
* Files inside are scanned flat (no nested site subdirs).
|
|
171
193
|
*/
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (Number.isNaN(num)) {
|
|
185
|
-
throw new Error(`Argument "${argDef.name}" must be a valid number. Received: "${val}"`);
|
|
186
|
-
}
|
|
187
|
-
result[argDef.name] = num;
|
|
188
|
-
}
|
|
189
|
-
else if (argDef.type === 'boolean' || argDef.type === 'bool') {
|
|
190
|
-
if (typeof val === 'string') {
|
|
191
|
-
const lower = val.toLowerCase();
|
|
192
|
-
if (lower === 'true' || lower === '1')
|
|
193
|
-
result[argDef.name] = true;
|
|
194
|
-
else if (lower === 'false' || lower === '0')
|
|
195
|
-
result[argDef.name] = false;
|
|
196
|
-
else
|
|
197
|
-
throw new Error(`Argument "${argDef.name}" must be a boolean (true/false). Received: "${val}"`);
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
result[argDef.name] = Boolean(val);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
// 3. Choices validation
|
|
204
|
-
const coercedVal = result[argDef.name];
|
|
205
|
-
if (argDef.choices && argDef.choices.length > 0) {
|
|
206
|
-
// Only stringent check for string/number types against choices array
|
|
207
|
-
if (!argDef.choices.map(String).includes(String(coercedVal))) {
|
|
208
|
-
throw new Error(`Argument "${argDef.name}" must be one of: ${argDef.choices.join(', ')}. Received: "${coercedVal}"`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
else if (argDef.default !== undefined) {
|
|
213
|
-
// Set default if value is missing
|
|
214
|
-
result[argDef.name] = argDef.default;
|
|
215
|
-
}
|
|
194
|
+
export async function discoverPlugins() {
|
|
195
|
+
try {
|
|
196
|
+
await fs.promises.access(PLUGINS_DIR);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const entries = await fs.promises.readdir(PLUGINS_DIR, { withFileTypes: true });
|
|
202
|
+
for (const entry of entries) {
|
|
203
|
+
if (!entry.isDirectory())
|
|
204
|
+
continue;
|
|
205
|
+
await discoverPluginDir(path.join(PLUGINS_DIR, entry.name), entry.name);
|
|
216
206
|
}
|
|
217
|
-
return result;
|
|
218
207
|
}
|
|
219
208
|
/**
|
|
220
|
-
*
|
|
209
|
+
* Flat scan: read yaml/ts files directly in a plugin directory.
|
|
210
|
+
* Unlike discoverClisFromFs, this does NOT expect nested site subdirectories.
|
|
221
211
|
*/
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
// Lazy-load TS module on first execution
|
|
232
|
-
const internal = cmd;
|
|
233
|
-
if (internal._lazy && internal._modulePath) {
|
|
234
|
-
const modulePath = internal._modulePath;
|
|
235
|
-
if (!_loadedModules.has(modulePath)) {
|
|
236
|
-
try {
|
|
237
|
-
await import(`file://${modulePath}`);
|
|
238
|
-
_loadedModules.add(modulePath);
|
|
239
|
-
}
|
|
240
|
-
catch (err) {
|
|
241
|
-
throw new AdapterLoadError(`Failed to load adapter module ${modulePath}: ${err.message}`, 'Check that the adapter file exists and has no syntax errors.');
|
|
242
|
-
}
|
|
212
|
+
async function discoverPluginDir(dir, site) {
|
|
213
|
+
const files = await fs.promises.readdir(dir);
|
|
214
|
+
const fileSet = new Set(files);
|
|
215
|
+
const promises = [];
|
|
216
|
+
for (const file of files) {
|
|
217
|
+
const filePath = path.join(dir, file);
|
|
218
|
+
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
|
|
219
|
+
promises.push(registerYamlCli(filePath, site));
|
|
243
220
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
221
|
+
else if (file.endsWith('.js') && !file.endsWith('.d.js')) {
|
|
222
|
+
if (!(await isCliModule(filePath)))
|
|
223
|
+
continue;
|
|
224
|
+
promises.push(import(pathToFileURL(filePath).href).catch((err) => {
|
|
225
|
+
log.warn(`Plugin ${site}/${file}: ${getErrorMessage(err)}`);
|
|
226
|
+
}));
|
|
250
227
|
}
|
|
251
|
-
if (
|
|
252
|
-
|
|
228
|
+
else if (file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts')) {
|
|
229
|
+
// Skip .ts if a compiled .js sibling exists (production mode can't load .ts)
|
|
230
|
+
const jsFile = file.replace(/\.ts$/, '.js');
|
|
231
|
+
if (fileSet.has(jsFile))
|
|
232
|
+
continue;
|
|
233
|
+
if (!(await isCliModule(filePath)))
|
|
234
|
+
continue;
|
|
235
|
+
promises.push(import(pathToFileURL(filePath).href).catch((err) => {
|
|
236
|
+
log.warn(`Plugin ${site}/${file}: ${getErrorMessage(err)}`);
|
|
237
|
+
}));
|
|
253
238
|
}
|
|
254
239
|
}
|
|
255
|
-
|
|
256
|
-
|
|
240
|
+
await Promise.all(promises);
|
|
241
|
+
}
|
|
242
|
+
async function isCliModule(filePath) {
|
|
243
|
+
try {
|
|
244
|
+
const source = await fs.promises.readFile(filePath, 'utf-8');
|
|
245
|
+
return CLI_MODULE_PATTERN.test(source);
|
|
257
246
|
}
|
|
258
|
-
|
|
259
|
-
|
|
247
|
+
catch (err) {
|
|
248
|
+
log.warn(`Failed to inspect module ${filePath}: ${getErrorMessage(err)}`);
|
|
249
|
+
return false;
|
|
260
250
|
}
|
|
261
|
-
throw new Error(`Command ${cmd.site}/${cmd.name} has no func or pipeline`);
|
|
262
251
|
}
|
package/dist/doctor.js
CHANGED
|
@@ -26,11 +26,13 @@ export async function checkConnectivity(opts) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
export async function runBrowserDoctor(opts = {}) {
|
|
29
|
-
|
|
29
|
+
// Run the live connectivity check first — it may auto-start the daemon as a
|
|
30
|
+
// side-effect, so we read daemon status only *after* all side-effects settle.
|
|
30
31
|
let connectivity;
|
|
31
32
|
if (opts.live) {
|
|
32
33
|
connectivity = await checkConnectivity();
|
|
33
34
|
}
|
|
35
|
+
const status = await checkDaemonStatus();
|
|
34
36
|
const sessions = opts.sessions && status.running && status.extensionConnected
|
|
35
37
|
? await listSessions()
|
|
36
38
|
: undefined;
|
package/dist/doctor.test.js
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
const { mockCheckDaemonStatus, mockListSessions, mockConnect, mockClose } = vi.hoisted(() => ({
|
|
3
|
+
mockCheckDaemonStatus: vi.fn(),
|
|
4
|
+
mockListSessions: vi.fn(),
|
|
5
|
+
mockConnect: vi.fn(),
|
|
6
|
+
mockClose: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('./browser/discover.js', () => ({
|
|
9
|
+
checkDaemonStatus: mockCheckDaemonStatus,
|
|
10
|
+
}));
|
|
11
|
+
vi.mock('./browser/daemon-client.js', () => ({
|
|
12
|
+
listSessions: mockListSessions,
|
|
13
|
+
}));
|
|
14
|
+
vi.mock('./browser/index.js', () => ({
|
|
15
|
+
BrowserBridge: class {
|
|
16
|
+
connect = mockConnect;
|
|
17
|
+
close = mockClose;
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
import { renderBrowserDoctorReport, runBrowserDoctor } from './doctor.js';
|
|
3
21
|
describe('doctor report rendering', () => {
|
|
4
22
|
const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
});
|
|
5
26
|
it('renders OK-style report when daemon and extension connected', () => {
|
|
6
27
|
const text = strip(renderBrowserDoctorReport({
|
|
7
28
|
daemonRunning: true,
|
|
@@ -48,4 +69,27 @@ describe('doctor report rendering', () => {
|
|
|
48
69
|
}));
|
|
49
70
|
expect(text).toContain('[SKIP] Connectivity: not tested (use --live)');
|
|
50
71
|
});
|
|
72
|
+
it('reports consistent status when live check auto-starts the daemon', async () => {
|
|
73
|
+
// With the reordered flow, checkDaemonStatus is called only ONCE — after
|
|
74
|
+
// the connectivity check that may auto-start the daemon.
|
|
75
|
+
mockCheckDaemonStatus.mockResolvedValueOnce({ running: true, extensionConnected: false });
|
|
76
|
+
mockConnect.mockRejectedValueOnce(new Error('Daemon is running but the Browser Extension is not connected.\n' +
|
|
77
|
+
'Please install and enable the opencli Browser Bridge extension in Chrome.'));
|
|
78
|
+
const report = await runBrowserDoctor({ live: true });
|
|
79
|
+
// Status reflects the post-connectivity state (daemon running)
|
|
80
|
+
expect(report.daemonRunning).toBe(true);
|
|
81
|
+
expect(report.extensionConnected).toBe(false);
|
|
82
|
+
// checkDaemonStatus should only be called once
|
|
83
|
+
expect(mockCheckDaemonStatus).toHaveBeenCalledTimes(1);
|
|
84
|
+
// Should NOT report "daemon not running" since it IS running after live check
|
|
85
|
+
expect(report.issues).not.toContain(expect.stringContaining('Daemon is not running'));
|
|
86
|
+
// Should report extension not connected
|
|
87
|
+
expect(report.issues).toEqual(expect.arrayContaining([
|
|
88
|
+
expect.stringContaining('Chrome extension is not connected'),
|
|
89
|
+
]));
|
|
90
|
+
// Should report connectivity failure
|
|
91
|
+
expect(report.issues).toEqual(expect.arrayContaining([
|
|
92
|
+
expect.stringContaining('Browser connectivity test failed'),
|
|
93
|
+
]));
|
|
94
|
+
});
|
|
51
95
|
});
|
package/dist/download/index.d.ts
CHANGED
|
@@ -23,13 +23,9 @@ export interface BrowserCookie {
|
|
|
23
23
|
httpOnly?: boolean;
|
|
24
24
|
expirationDate?: number;
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Check if yt-dlp is available in PATH.
|
|
28
|
-
*/
|
|
26
|
+
/** Check if yt-dlp is available in PATH. */
|
|
29
27
|
export declare function checkYtdlp(): boolean;
|
|
30
|
-
/**
|
|
31
|
-
* Check if ffmpeg is available in PATH.
|
|
32
|
-
*/
|
|
28
|
+
/** Check if ffmpeg is available in PATH. */
|
|
33
29
|
export declare function checkFfmpeg(): boolean;
|
|
34
30
|
/**
|
|
35
31
|
* Detect content type from URL and optional headers.
|
package/dist/download/index.js
CHANGED
|
@@ -1,42 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Download utilities: HTTP downloads, yt-dlp wrapper, format conversion.
|
|
3
3
|
*/
|
|
4
|
-
import { spawn
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
5
|
import * as fs from 'node:fs';
|
|
6
6
|
import * as path from 'node:path';
|
|
7
7
|
import * as https from 'node:https';
|
|
8
8
|
import * as http from 'node:http';
|
|
9
9
|
import * as os from 'node:os';
|
|
10
10
|
import { URL } from 'node:url';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
11
|
+
import { isBinaryInstalled } from '../external.js';
|
|
12
|
+
/** Check if yt-dlp is available in PATH. */
|
|
14
13
|
export function checkYtdlp() {
|
|
15
|
-
|
|
16
|
-
execSync('yt-dlp --version', { encoding: 'utf-8', stdio: 'pipe' });
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
14
|
+
return isBinaryInstalled('yt-dlp');
|
|
22
15
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Check if ffmpeg is available in PATH.
|
|
25
|
-
*/
|
|
16
|
+
/** Check if ffmpeg is available in PATH. */
|
|
26
17
|
export function checkFfmpeg() {
|
|
27
|
-
|
|
28
|
-
execSync('ffmpeg -version', { encoding: 'utf-8', stdio: 'pipe' });
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
18
|
+
return isBinaryInstalled('ffmpeg');
|
|
34
19
|
}
|
|
20
|
+
/** Domains that host video content and can be downloaded via yt-dlp. */
|
|
21
|
+
const VIDEO_PLATFORM_DOMAINS = [
|
|
22
|
+
'youtube.com', 'youtu.be', 'bilibili.com', 'twitter.com',
|
|
23
|
+
'x.com', 'tiktok.com', 'vimeo.com', 'twitch.tv',
|
|
24
|
+
];
|
|
25
|
+
const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico', '.bmp', '.avif']);
|
|
26
|
+
const VIDEO_EXTENSIONS = new Set(['.mp4', '.webm', '.avi', '.mov', '.mkv', '.flv', '.m3u8', '.ts']);
|
|
27
|
+
const DOC_EXTENSIONS = new Set(['.html', '.htm', '.json', '.xml', '.txt', '.md', '.markdown']);
|
|
35
28
|
/**
|
|
36
29
|
* Detect content type from URL and optional headers.
|
|
37
30
|
*/
|
|
38
31
|
export function detectContentType(url, contentType) {
|
|
39
|
-
// Check content-type header first
|
|
40
32
|
if (contentType) {
|
|
41
33
|
if (contentType.startsWith('image/'))
|
|
42
34
|
return 'image';
|
|
@@ -45,28 +37,16 @@ export function detectContentType(url, contentType) {
|
|
|
45
37
|
if (contentType.startsWith('text/') || contentType.includes('json') || contentType.includes('xml'))
|
|
46
38
|
return 'document';
|
|
47
39
|
}
|
|
48
|
-
// Detect from URL
|
|
49
40
|
const urlLower = url.toLowerCase();
|
|
50
41
|
const ext = path.extname(new URL(url).pathname).toLowerCase();
|
|
51
|
-
|
|
52
|
-
if (['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico', '.bmp', '.avif'].includes(ext)) {
|
|
42
|
+
if (IMAGE_EXTENSIONS.has(ext))
|
|
53
43
|
return 'image';
|
|
54
|
-
|
|
55
|
-
// Video extensions
|
|
56
|
-
if (['.mp4', '.webm', '.avi', '.mov', '.mkv', '.flv', '.m3u8', '.ts'].includes(ext)) {
|
|
44
|
+
if (VIDEO_EXTENSIONS.has(ext))
|
|
57
45
|
return 'video';
|
|
58
|
-
|
|
59
|
-
// Video platforms (need yt-dlp)
|
|
60
|
-
if (urlLower.includes('youtube.com') || urlLower.includes('youtu.be') ||
|
|
61
|
-
urlLower.includes('bilibili.com') || urlLower.includes('twitter.com') ||
|
|
62
|
-
urlLower.includes('x.com') || urlLower.includes('tiktok.com') ||
|
|
63
|
-
urlLower.includes('vimeo.com') || urlLower.includes('twitch.tv')) {
|
|
46
|
+
if (VIDEO_PLATFORM_DOMAINS.some(d => urlLower.includes(d)))
|
|
64
47
|
return 'video';
|
|
65
|
-
|
|
66
|
-
// Document extensions
|
|
67
|
-
if (['.html', '.htm', '.json', '.xml', '.txt', '.md', '.markdown'].includes(ext)) {
|
|
48
|
+
if (DOC_EXTENSIONS.has(ext))
|
|
68
49
|
return 'document';
|
|
69
|
-
}
|
|
70
50
|
return 'binary';
|
|
71
51
|
}
|
|
72
52
|
/**
|
|
@@ -74,14 +54,7 @@ export function detectContentType(url, contentType) {
|
|
|
74
54
|
*/
|
|
75
55
|
export function requiresYtdlp(url) {
|
|
76
56
|
const urlLower = url.toLowerCase();
|
|
77
|
-
return (urlLower.includes(
|
|
78
|
-
urlLower.includes('youtu.be') ||
|
|
79
|
-
urlLower.includes('bilibili.com/video') ||
|
|
80
|
-
urlLower.includes('twitter.com') ||
|
|
81
|
-
urlLower.includes('x.com') ||
|
|
82
|
-
urlLower.includes('tiktok.com') ||
|
|
83
|
-
urlLower.includes('vimeo.com') ||
|
|
84
|
-
urlLower.includes('twitch.tv'));
|
|
57
|
+
return VIDEO_PLATFORM_DOMAINS.some(d => urlLower.includes(d));
|
|
85
58
|
}
|
|
86
59
|
/**
|
|
87
60
|
* HTTP download with progress callback.
|
package/dist/engine.test.d.ts
CHANGED
package/dist/engine.test.js
CHANGED
|
@@ -1,14 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { discoverClis, discoverPlugins, PLUGINS_DIR } from './discovery.js';
|
|
3
|
+
import { executeCommand } from './execution.js';
|
|
4
|
+
import { getRegistry, cli, Strategy } from './registry.js';
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
7
|
describe('discoverClis', () => {
|
|
8
8
|
it('handles non-existent directories gracefully', async () => {
|
|
9
9
|
// Should not throw for missing directories
|
|
10
10
|
await expect(discoverClis('/tmp/nonexistent-opencli-test-dir')).resolves.not.toThrow();
|
|
11
11
|
});
|
|
12
|
+
it('imports only CLI command modules during filesystem discovery', async () => {
|
|
13
|
+
const tempRoot = await fs.promises.mkdtemp(path.join('/tmp', 'opencli-discovery-'));
|
|
14
|
+
const siteDir = path.join(tempRoot, 'temp-site');
|
|
15
|
+
const helperPath = path.join(siteDir, 'helper.ts');
|
|
16
|
+
const commandPath = path.join(siteDir, 'hello.ts');
|
|
17
|
+
try {
|
|
18
|
+
await fs.promises.mkdir(siteDir, { recursive: true });
|
|
19
|
+
await fs.promises.writeFile(helperPath, `
|
|
20
|
+
globalThis.__opencli_helper_loaded__ = true;
|
|
21
|
+
export const helper = true;
|
|
22
|
+
`);
|
|
23
|
+
await fs.promises.writeFile(commandPath, `
|
|
24
|
+
import { cli, Strategy } from '${path.join(process.cwd(), 'src', 'registry.ts')}';
|
|
25
|
+
cli({
|
|
26
|
+
site: 'temp-site',
|
|
27
|
+
name: 'hello',
|
|
28
|
+
description: 'hello command',
|
|
29
|
+
strategy: Strategy.PUBLIC,
|
|
30
|
+
browser: false,
|
|
31
|
+
func: async () => [{ ok: true }],
|
|
32
|
+
});
|
|
33
|
+
`);
|
|
34
|
+
delete globalThis.__opencli_helper_loaded__;
|
|
35
|
+
await discoverClis(tempRoot);
|
|
36
|
+
expect(globalThis.__opencli_helper_loaded__).toBeUndefined();
|
|
37
|
+
expect(getRegistry().get('temp-site/hello')).toBeDefined();
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
delete globalThis.__opencli_helper_loaded__;
|
|
41
|
+
await fs.promises.rm(tempRoot, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('discoverPlugins', () => {
|
|
46
|
+
const testPluginDir = path.join(PLUGINS_DIR, '__test-plugin__');
|
|
47
|
+
const yamlPath = path.join(testPluginDir, 'greeting.yaml');
|
|
48
|
+
afterEach(async () => {
|
|
49
|
+
try {
|
|
50
|
+
await fs.promises.rm(testPluginDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
});
|
|
54
|
+
it('discovers YAML plugins from ~/.opencli/plugins/', async () => {
|
|
55
|
+
// Create a simple YAML adapter in the plugins directory
|
|
56
|
+
await fs.promises.mkdir(testPluginDir, { recursive: true });
|
|
57
|
+
await fs.promises.writeFile(yamlPath, `
|
|
58
|
+
site: __test-plugin__
|
|
59
|
+
name: greeting
|
|
60
|
+
description: Test plugin greeting
|
|
61
|
+
strategy: public
|
|
62
|
+
browser: false
|
|
63
|
+
|
|
64
|
+
pipeline:
|
|
65
|
+
- evaluate: "() => [{ message: 'hello from plugin' }]"
|
|
66
|
+
|
|
67
|
+
columns: [message]
|
|
68
|
+
`);
|
|
69
|
+
await discoverPlugins();
|
|
70
|
+
const registry = getRegistry();
|
|
71
|
+
const cmd = registry.get('__test-plugin__/greeting');
|
|
72
|
+
expect(cmd).toBeDefined();
|
|
73
|
+
expect(cmd.site).toBe('__test-plugin__');
|
|
74
|
+
expect(cmd.name).toBe('greeting');
|
|
75
|
+
expect(cmd.description).toBe('Test plugin greeting');
|
|
76
|
+
});
|
|
77
|
+
it('handles non-existent plugins directory gracefully', async () => {
|
|
78
|
+
// discoverPlugins should not throw if ~/.opencli/plugins/ does not exist
|
|
79
|
+
await expect(discoverPlugins()).resolves.not.toThrow();
|
|
80
|
+
});
|
|
12
81
|
});
|
|
13
82
|
describe('executeCommand', () => {
|
|
14
83
|
it('accepts kebab-case option names after Commander camelCases them', async () => {
|
|
@@ -23,7 +92,7 @@ describe('executeCommand', () => {
|
|
|
23
92
|
],
|
|
24
93
|
func: async (_page, kwargs) => [{ noteId: kwargs['note-id'] }],
|
|
25
94
|
});
|
|
26
|
-
const result = await executeCommand(cmd,
|
|
95
|
+
const result = await executeCommand(cmd, { 'note-id': 'abc123' });
|
|
27
96
|
expect(result).toEqual([{ noteId: 'abc123' }]);
|
|
28
97
|
});
|
|
29
98
|
it('executes a command with func', async () => {
|
|
@@ -37,7 +106,7 @@ describe('executeCommand', () => {
|
|
|
37
106
|
return [{ title: kwargs.query ?? 'default' }];
|
|
38
107
|
},
|
|
39
108
|
});
|
|
40
|
-
const result = await executeCommand(cmd,
|
|
109
|
+
const result = await executeCommand(cmd, { query: 'hello' });
|
|
41
110
|
expect(result).toEqual([{ title: 'hello' }]);
|
|
42
111
|
});
|
|
43
112
|
it('executes a command with pipeline', async () => {
|
|
@@ -53,7 +122,7 @@ describe('executeCommand', () => {
|
|
|
53
122
|
],
|
|
54
123
|
});
|
|
55
124
|
// Pipeline commands require page for evaluate step, so we'll test the error path
|
|
56
|
-
await expect(executeCommand(cmd,
|
|
125
|
+
await expect(executeCommand(cmd, {})).rejects.toThrow();
|
|
57
126
|
});
|
|
58
127
|
it('throws for command with no func or pipeline', async () => {
|
|
59
128
|
const cmd = cli({
|
|
@@ -62,7 +131,7 @@ describe('executeCommand', () => {
|
|
|
62
131
|
description: 'empty command',
|
|
63
132
|
browser: false,
|
|
64
133
|
});
|
|
65
|
-
await expect(executeCommand(cmd,
|
|
134
|
+
await expect(executeCommand(cmd, {})).rejects.toThrow('has no func or pipeline');
|
|
66
135
|
});
|
|
67
136
|
it('passes debug flag to func', async () => {
|
|
68
137
|
let receivedDebug = false;
|
|
@@ -76,7 +145,7 @@ describe('executeCommand', () => {
|
|
|
76
145
|
return [];
|
|
77
146
|
},
|
|
78
147
|
});
|
|
79
|
-
await executeCommand(cmd,
|
|
148
|
+
await executeCommand(cmd, {}, true);
|
|
80
149
|
expect(receivedDebug).toBe(true);
|
|
81
150
|
});
|
|
82
151
|
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command execution: validates args, manages browser sessions, runs commands.
|
|
3
|
+
*
|
|
4
|
+
* This is the single entry point for executing any CLI command. It handles:
|
|
5
|
+
* 1. Argument validation and coercion
|
|
6
|
+
* 2. Browser session lifecycle (if needed)
|
|
7
|
+
* 3. Domain pre-navigation for cookie/header strategies
|
|
8
|
+
* 4. Timeout enforcement
|
|
9
|
+
* 5. Lazy-loading of TS modules from manifest
|
|
10
|
+
*/
|
|
11
|
+
import { type CliCommand, type Arg } from './registry.js';
|
|
12
|
+
type CommandArgs = Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Validates and coerces arguments based on the command's Arg definitions.
|
|
15
|
+
*/
|
|
16
|
+
export declare function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: CommandArgs): CommandArgs;
|
|
17
|
+
/**
|
|
18
|
+
* Execute a CLI command. Automatically manages browser sessions when needed.
|
|
19
|
+
*
|
|
20
|
+
* This is the unified entry point — callers don't need to care about
|
|
21
|
+
* whether the command requires a browser or not.
|
|
22
|
+
*/
|
|
23
|
+
export declare function executeCommand(cmd: CliCommand, rawKwargs: CommandArgs, debug?: boolean): Promise<unknown>;
|
|
24
|
+
export {};
|