@jackwener/opencli 1.1.1 → 1.2.1
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/.github/workflows/build-extension.yml +3 -3
- package/.github/workflows/ci.yml +6 -6
- package/.github/workflows/doc-check.yml +3 -3
- package/.github/workflows/e2e-headed.yml +2 -2
- package/.github/workflows/pkg-pr-new.yml +2 -2
- package/.github/workflows/release.yml +3 -3
- package/.github/workflows/security.yml +2 -2
- package/CONTRIBUTING.md +39 -1
- package/README.md +13 -10
- package/README.zh-CN.md +43 -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 +4 -2
- package/dist/browser/daemon-client.js +17 -4
- 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/mcp.js +3 -1
- package/dist/browser/page.d.ts +14 -24
- package/dist/browser/page.js +46 -6
- 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/daemon.js +21 -0
- 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 +4 -2
- package/dist/pipeline/executor.js +54 -15
- 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/dist/background.js +91 -23
- package/extension/src/background.ts +82 -29
- package/extension/src/cdp.ts +42 -1
- 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 +21 -5
- 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/mcp.ts +3 -1
- package/src/browser/page.ts +57 -21
- 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/daemon.ts +23 -0
- 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 +68 -19
- 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/dist/commanderAdapter.js
CHANGED
|
@@ -15,6 +15,9 @@ import { formatRegistryHelpText } from './serialization.js';
|
|
|
15
15
|
import { render as renderOutput } from './output.js';
|
|
16
16
|
import { executeCommand } from './execution.js';
|
|
17
17
|
import { CliError } from './errors.js';
|
|
18
|
+
function getErrorMessage(error) {
|
|
19
|
+
return error instanceof Error ? error.message : String(error);
|
|
20
|
+
}
|
|
18
21
|
/**
|
|
19
22
|
* Register a single CliCommand as a Commander subcommand.
|
|
20
23
|
*/
|
|
@@ -46,6 +49,7 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
46
49
|
subCmd.addHelpText('after', formatRegistryHelpText(cmd));
|
|
47
50
|
subCmd.action(async (...actionArgs) => {
|
|
48
51
|
const actionOpts = actionArgs[positionalArgs.length] ?? {};
|
|
52
|
+
const optionsRecord = typeof actionOpts === 'object' && actionOpts !== null ? actionOpts : {};
|
|
49
53
|
const startTime = Date.now();
|
|
50
54
|
// ── Collect kwargs ──────────────────────────────────────────────────
|
|
51
55
|
const kwargs = {};
|
|
@@ -58,21 +62,23 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
58
62
|
if (arg.positional)
|
|
59
63
|
continue;
|
|
60
64
|
const camelName = arg.name.replace(/-([a-z])/g, (_m, ch) => ch.toUpperCase());
|
|
61
|
-
const v =
|
|
65
|
+
const v = optionsRecord[arg.name] ?? optionsRecord[camelName];
|
|
62
66
|
if (v !== undefined)
|
|
63
67
|
kwargs[arg.name] = v;
|
|
64
68
|
}
|
|
65
69
|
// ── Execute + render ────────────────────────────────────────────────
|
|
66
70
|
try {
|
|
67
|
-
|
|
71
|
+
const verbose = optionsRecord.verbose === true;
|
|
72
|
+
const format = typeof optionsRecord.format === 'string' ? optionsRecord.format : 'table';
|
|
73
|
+
if (verbose)
|
|
68
74
|
process.env.OPENCLI_VERBOSE = '1';
|
|
69
|
-
const result = await executeCommand(cmd, kwargs,
|
|
70
|
-
if (
|
|
75
|
+
const result = await executeCommand(cmd, kwargs, verbose);
|
|
76
|
+
if (verbose && (!result || (Array.isArray(result) && result.length === 0))) {
|
|
71
77
|
console.error(chalk.yellow('[Verbose] Warning: Command returned an empty result.'));
|
|
72
78
|
}
|
|
73
79
|
const resolved = getRegistry().get(fullName(cmd)) ?? cmd;
|
|
74
80
|
renderOutput(result, {
|
|
75
|
-
fmt:
|
|
81
|
+
fmt: format,
|
|
76
82
|
columns: resolved.columns,
|
|
77
83
|
title: `${resolved.site}/${resolved.name}`,
|
|
78
84
|
elapsed: (Date.now() - startTime) / 1000,
|
|
@@ -86,11 +92,11 @@ export function registerCommandToProgram(siteCmd, cmd) {
|
|
|
86
92
|
if (err.hint)
|
|
87
93
|
console.error(chalk.yellow(`Hint: ${err.hint}`));
|
|
88
94
|
}
|
|
89
|
-
else if (
|
|
95
|
+
else if (optionsRecord.verbose === true && err instanceof Error && err.stack) {
|
|
90
96
|
console.error(chalk.red(err.stack));
|
|
91
97
|
}
|
|
92
98
|
else {
|
|
93
|
-
console.error(chalk.red(`Error: ${err
|
|
99
|
+
console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
|
|
94
100
|
}
|
|
95
101
|
process.exitCode = 1;
|
|
96
102
|
}
|
package/dist/daemon.js
CHANGED
|
@@ -118,6 +118,25 @@ const wss = new WebSocketServer({ server: httpServer, path: '/ext' });
|
|
|
118
118
|
wss.on('connection', (ws) => {
|
|
119
119
|
console.error('[daemon] Extension connected');
|
|
120
120
|
extensionWs = ws;
|
|
121
|
+
// ── Heartbeat: ping every 15s, close if 2 pongs missed ──
|
|
122
|
+
let missedPongs = 0;
|
|
123
|
+
const heartbeatInterval = setInterval(() => {
|
|
124
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
125
|
+
clearInterval(heartbeatInterval);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (missedPongs >= 2) {
|
|
129
|
+
console.error('[daemon] Extension heartbeat lost, closing connection');
|
|
130
|
+
clearInterval(heartbeatInterval);
|
|
131
|
+
ws.terminate();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
missedPongs++;
|
|
135
|
+
ws.ping();
|
|
136
|
+
}, 15000);
|
|
137
|
+
ws.on('pong', () => {
|
|
138
|
+
missedPongs = 0;
|
|
139
|
+
});
|
|
121
140
|
ws.on('message', (data) => {
|
|
122
141
|
try {
|
|
123
142
|
const msg = JSON.parse(data.toString());
|
|
@@ -142,6 +161,7 @@ wss.on('connection', (ws) => {
|
|
|
142
161
|
});
|
|
143
162
|
ws.on('close', () => {
|
|
144
163
|
console.error('[daemon] Extension disconnected');
|
|
164
|
+
clearInterval(heartbeatInterval);
|
|
145
165
|
if (extensionWs === ws) {
|
|
146
166
|
extensionWs = null;
|
|
147
167
|
// Reject all pending requests since the extension is gone
|
|
@@ -153,6 +173,7 @@ wss.on('connection', (ws) => {
|
|
|
153
173
|
}
|
|
154
174
|
});
|
|
155
175
|
ws.on('error', () => {
|
|
176
|
+
clearInterval(heartbeatInterval);
|
|
156
177
|
if (extensionWs === ws)
|
|
157
178
|
extensionWs = null;
|
|
158
179
|
});
|
package/dist/discovery.d.ts
CHANGED
|
@@ -7,8 +7,16 @@
|
|
|
7
7
|
* TS modules are loaded lazily only when their command is executed.
|
|
8
8
|
* 2. FALLBACK (filesystem scan): Traditional runtime discovery for development.
|
|
9
9
|
*/
|
|
10
|
+
/** Plugins directory: ~/.opencli/plugins/ */
|
|
11
|
+
export declare const PLUGINS_DIR: string;
|
|
10
12
|
/**
|
|
11
13
|
* Discover and register CLI commands.
|
|
12
14
|
* Uses pre-compiled manifest when available for instant startup.
|
|
13
15
|
*/
|
|
14
16
|
export declare function discoverClis(...dirs: string[]): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Discover and register plugins from ~/.opencli/plugins/.
|
|
19
|
+
* Each subdirectory is treated as a plugin (site = directory name).
|
|
20
|
+
* Files inside are scanned flat (no nested site subdirs).
|
|
21
|
+
*/
|
|
22
|
+
export declare function discoverPlugins(): Promise<void>;
|
package/dist/discovery.js
CHANGED
|
@@ -8,10 +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
16
|
import { log } from './logger.js';
|
|
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
|
+
}
|
|
15
32
|
/**
|
|
16
33
|
* Discover and register CLI commands.
|
|
17
34
|
* Uses pre-compiled manifest when available for instant startup.
|
|
@@ -43,7 +60,7 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
43
60
|
for (const entry of manifest) {
|
|
44
61
|
if (entry.type === 'yaml') {
|
|
45
62
|
// YAML pipelines fully inlined in manifest — register directly
|
|
46
|
-
const strategy =
|
|
63
|
+
const strategy = parseStrategy(entry.strategy);
|
|
47
64
|
const cmd = {
|
|
48
65
|
site: entry.site,
|
|
49
66
|
name: entry.name,
|
|
@@ -56,13 +73,14 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
56
73
|
pipeline: entry.pipeline,
|
|
57
74
|
timeoutSeconds: entry.timeout,
|
|
58
75
|
source: `manifest:${entry.site}/${entry.name}`,
|
|
76
|
+
navigateBefore: entry.navigateBefore,
|
|
59
77
|
};
|
|
60
78
|
registerCommand(cmd);
|
|
61
79
|
}
|
|
62
80
|
else if (entry.type === 'ts' && entry.modulePath) {
|
|
63
81
|
// TS adapters: register a lightweight stub.
|
|
64
82
|
// The actual module is loaded lazily on first executeCommand().
|
|
65
|
-
const strategy =
|
|
83
|
+
const strategy = parseStrategy(entry.strategy ?? 'cookie');
|
|
66
84
|
const modulePath = path.resolve(clisDir, entry.modulePath);
|
|
67
85
|
const cmd = {
|
|
68
86
|
site: entry.site,
|
|
@@ -75,6 +93,7 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
75
93
|
columns: entry.columns,
|
|
76
94
|
timeoutSeconds: entry.timeout,
|
|
77
95
|
source: modulePath,
|
|
96
|
+
navigateBefore: entry.navigateBefore,
|
|
78
97
|
_lazy: true,
|
|
79
98
|
_modulePath: modulePath,
|
|
80
99
|
};
|
|
@@ -83,7 +102,7 @@ async function loadFromManifest(manifestPath, clisDir) {
|
|
|
83
102
|
}
|
|
84
103
|
}
|
|
85
104
|
catch (err) {
|
|
86
|
-
log.warn(`Failed to load manifest ${manifestPath}: ${err
|
|
105
|
+
log.warn(`Failed to load manifest ${manifestPath}: ${getErrorMessage(err)}`);
|
|
87
106
|
}
|
|
88
107
|
}
|
|
89
108
|
/**
|
|
@@ -111,8 +130,10 @@ async function discoverClisFromFs(dir) {
|
|
|
111
130
|
}
|
|
112
131
|
else if ((file.endsWith('.js') && !file.endsWith('.d.js')) ||
|
|
113
132
|
(file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts'))) {
|
|
114
|
-
|
|
115
|
-
|
|
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)}`);
|
|
116
137
|
}));
|
|
117
138
|
}
|
|
118
139
|
}
|
|
@@ -123,16 +144,17 @@ async function registerYamlCli(filePath, defaultSite) {
|
|
|
123
144
|
try {
|
|
124
145
|
const raw = await fs.promises.readFile(filePath, 'utf-8');
|
|
125
146
|
const def = yaml.load(raw);
|
|
126
|
-
if (!def
|
|
147
|
+
if (!isRecord(def))
|
|
127
148
|
return;
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
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);
|
|
133
155
|
const args = [];
|
|
134
|
-
if (
|
|
135
|
-
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)) {
|
|
136
158
|
args.push({
|
|
137
159
|
name: argName,
|
|
138
160
|
type: argDef?.type ?? 'str',
|
|
@@ -147,19 +169,83 @@ async function registerYamlCli(filePath, defaultSite) {
|
|
|
147
169
|
const cmd = {
|
|
148
170
|
site,
|
|
149
171
|
name,
|
|
150
|
-
description:
|
|
151
|
-
domain:
|
|
172
|
+
description: cliDef.description ?? '',
|
|
173
|
+
domain: cliDef.domain,
|
|
152
174
|
strategy,
|
|
153
175
|
browser,
|
|
154
176
|
args,
|
|
155
|
-
columns:
|
|
156
|
-
pipeline:
|
|
157
|
-
timeoutSeconds:
|
|
177
|
+
columns: cliDef.columns,
|
|
178
|
+
pipeline: cliDef.pipeline,
|
|
179
|
+
timeoutSeconds: cliDef.timeout,
|
|
158
180
|
source: filePath,
|
|
181
|
+
navigateBefore: cliDef.navigateBefore,
|
|
159
182
|
};
|
|
160
183
|
registerCommand(cmd);
|
|
161
184
|
}
|
|
162
185
|
catch (err) {
|
|
163
|
-
log.warn(`Failed to load ${filePath}: ${err
|
|
186
|
+
log.warn(`Failed to load ${filePath}: ${getErrorMessage(err)}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
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).
|
|
193
|
+
*/
|
|
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);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Flat scan: read yaml/ts files directly in a plugin directory.
|
|
210
|
+
* Unlike discoverClisFromFs, this does NOT expect nested site subdirectories.
|
|
211
|
+
*/
|
|
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));
|
|
220
|
+
}
|
|
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
|
+
}));
|
|
227
|
+
}
|
|
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
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
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);
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
log.warn(`Failed to inspect module ${filePath}: ${getErrorMessage(err)}`);
|
|
249
|
+
return false;
|
|
164
250
|
}
|
|
165
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/engine.test.d.ts
CHANGED
package/dist/engine.test.js
CHANGED
|
@@ -1,15 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect } from 'vitest';
|
|
5
|
-
import { discoverClis } from './discovery.js';
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { discoverClis, discoverPlugins, PLUGINS_DIR } from './discovery.js';
|
|
6
3
|
import { executeCommand } from './execution.js';
|
|
7
|
-
import { cli, Strategy } from './registry.js';
|
|
4
|
+
import { getRegistry, cli, Strategy } from './registry.js';
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
8
7
|
describe('discoverClis', () => {
|
|
9
8
|
it('handles non-existent directories gracefully', async () => {
|
|
10
9
|
// Should not throw for missing directories
|
|
11
10
|
await expect(discoverClis('/tmp/nonexistent-opencli-test-dir')).resolves.not.toThrow();
|
|
12
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
|
+
});
|
|
13
81
|
});
|
|
14
82
|
describe('executeCommand', () => {
|
|
15
83
|
it('accepts kebab-case option names after Commander camelCases them', async () => {
|
package/dist/execution.d.ts
CHANGED
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
* 5. Lazy-loading of TS modules from manifest
|
|
10
10
|
*/
|
|
11
11
|
import { type CliCommand, type Arg } from './registry.js';
|
|
12
|
+
type CommandArgs = Record<string, unknown>;
|
|
12
13
|
/**
|
|
13
14
|
* Validates and coerces arguments based on the command's Arg definitions.
|
|
14
15
|
*/
|
|
15
|
-
export declare function coerceAndValidateArgs(cmdArgs: Arg[], kwargs:
|
|
16
|
+
export declare function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: CommandArgs): CommandArgs;
|
|
16
17
|
/**
|
|
17
18
|
* Execute a CLI command. Automatically manages browser sessions when needed.
|
|
18
19
|
*
|
|
19
20
|
* This is the unified entry point — callers don't need to care about
|
|
20
21
|
* whether the command requires a browser or not.
|
|
21
22
|
*/
|
|
22
|
-
export declare function executeCommand(cmd: CliCommand, rawKwargs:
|
|
23
|
+
export declare function executeCommand(cmd: CliCommand, rawKwargs: CommandArgs, debug?: boolean): Promise<unknown>;
|
|
24
|
+
export {};
|
package/dist/execution.js
CHANGED
|
@@ -9,12 +9,16 @@
|
|
|
9
9
|
* 5. Lazy-loading of TS modules from manifest
|
|
10
10
|
*/
|
|
11
11
|
import { Strategy, getRegistry, fullName } from './registry.js';
|
|
12
|
-
import {
|
|
12
|
+
import { pathToFileURL } from 'node:url';
|
|
13
|
+
import { executePipeline } from './pipeline/index.js';
|
|
13
14
|
import { AdapterLoadError } from './errors.js';
|
|
14
15
|
import { shouldUseBrowserSession } from './capabilityRouting.js';
|
|
15
16
|
import { getBrowserFactory, browserSession, runWithTimeout, DEFAULT_BROWSER_COMMAND_TIMEOUT } from './runtime.js';
|
|
16
17
|
/** Set of TS module paths that have been loaded */
|
|
17
18
|
const _loadedModules = new Set();
|
|
19
|
+
function getErrorMessage(error) {
|
|
20
|
+
return error instanceof Error ? error.message : String(error);
|
|
21
|
+
}
|
|
18
22
|
/**
|
|
19
23
|
* Validates and coerces arguments based on the command's Arg definitions.
|
|
20
24
|
*/
|
|
@@ -73,11 +77,11 @@ async function runCommand(cmd, page, kwargs, debug) {
|
|
|
73
77
|
const modulePath = internal._modulePath;
|
|
74
78
|
if (!_loadedModules.has(modulePath)) {
|
|
75
79
|
try {
|
|
76
|
-
await import(
|
|
80
|
+
await import(pathToFileURL(modulePath).href);
|
|
77
81
|
_loadedModules.add(modulePath);
|
|
78
82
|
}
|
|
79
83
|
catch (err) {
|
|
80
|
-
throw new AdapterLoadError(`Failed to load adapter module ${modulePath}: ${err
|
|
84
|
+
throw new AdapterLoadError(`Failed to load adapter module ${modulePath}: ${getErrorMessage(err)}`, 'Check that the adapter file exists and has no syntax errors.');
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
87
|
// After loading, the module's cli() call will have updated the registry.
|
|
@@ -93,6 +97,24 @@ async function runCommand(cmd, page, kwargs, debug) {
|
|
|
93
97
|
return executePipeline(page, cmd.pipeline, { args: kwargs, debug });
|
|
94
98
|
throw new Error(`Command ${fullName(cmd)} has no func or pipeline`);
|
|
95
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve the pre-navigation URL for a command, or null to skip.
|
|
102
|
+
*
|
|
103
|
+
* COOKIE/HEADER strategies need the browser on the target domain so
|
|
104
|
+
* `fetch(url, { credentials: 'include' })` carries cookies.
|
|
105
|
+
* Adapters that handle their own navigation set `navigateBefore: false`.
|
|
106
|
+
*/
|
|
107
|
+
function resolvePreNav(cmd) {
|
|
108
|
+
if (cmd.navigateBefore === false)
|
|
109
|
+
return null;
|
|
110
|
+
if (typeof cmd.navigateBefore === 'string')
|
|
111
|
+
return cmd.navigateBefore;
|
|
112
|
+
// Default: pre-navigate for COOKIE/HEADER strategies with a domain
|
|
113
|
+
if ((cmd.strategy === Strategy.COOKIE || cmd.strategy === Strategy.HEADER) && cmd.domain) {
|
|
114
|
+
return `https://${cmd.domain}`;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
96
118
|
/**
|
|
97
119
|
* Execute a CLI command. Automatically manages browser sessions when needed.
|
|
98
120
|
*
|
|
@@ -105,15 +127,17 @@ export async function executeCommand(cmd, rawKwargs, debug = false) {
|
|
|
105
127
|
kwargs = coerceAndValidateArgs(cmd.args, rawKwargs);
|
|
106
128
|
}
|
|
107
129
|
catch (err) {
|
|
108
|
-
throw new Error(`[Argument Validation Error]\n${err
|
|
130
|
+
throw new Error(`[Argument Validation Error]\n${getErrorMessage(err)}`);
|
|
109
131
|
}
|
|
110
132
|
if (shouldUseBrowserSession(cmd)) {
|
|
111
133
|
const BrowserFactory = getBrowserFactory();
|
|
112
134
|
return browserSession(BrowserFactory, async (page) => {
|
|
113
|
-
//
|
|
114
|
-
|
|
135
|
+
// Pre-navigate to target domain for cookie/header context if needed.
|
|
136
|
+
// Each adapter controls this via `navigateBefore` (see CliCommand docs).
|
|
137
|
+
const preNavUrl = resolvePreNav(cmd);
|
|
138
|
+
if (preNavUrl) {
|
|
115
139
|
try {
|
|
116
|
-
await page.goto(
|
|
140
|
+
await page.goto(preNavUrl);
|
|
117
141
|
await page.wait(2);
|
|
118
142
|
}
|
|
119
143
|
catch { }
|