@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/src/execution.ts
CHANGED
|
@@ -11,19 +11,25 @@
|
|
|
11
11
|
|
|
12
12
|
import { type CliCommand, type InternalCliCommand, type Arg, Strategy, getRegistry, fullName } from './registry.js';
|
|
13
13
|
import type { IPage } from './types.js';
|
|
14
|
-
import {
|
|
14
|
+
import { pathToFileURL } from 'node:url';
|
|
15
|
+
import { executePipeline } from './pipeline/index.js';
|
|
15
16
|
import { AdapterLoadError } from './errors.js';
|
|
16
17
|
import { shouldUseBrowserSession } from './capabilityRouting.js';
|
|
17
18
|
import { getBrowserFactory, browserSession, runWithTimeout, DEFAULT_BROWSER_COMMAND_TIMEOUT } from './runtime.js';
|
|
18
19
|
|
|
19
20
|
/** Set of TS module paths that have been loaded */
|
|
20
21
|
const _loadedModules = new Set<string>();
|
|
22
|
+
type CommandArgs = Record<string, unknown>;
|
|
23
|
+
|
|
24
|
+
function getErrorMessage(error: unknown): string {
|
|
25
|
+
return error instanceof Error ? error.message : String(error);
|
|
26
|
+
}
|
|
21
27
|
|
|
22
28
|
/**
|
|
23
29
|
* Validates and coerces arguments based on the command's Arg definitions.
|
|
24
30
|
*/
|
|
25
|
-
export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs:
|
|
26
|
-
const result:
|
|
31
|
+
export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: CommandArgs): CommandArgs {
|
|
32
|
+
const result: CommandArgs = { ...kwargs };
|
|
27
33
|
|
|
28
34
|
for (const argDef of cmdArgs) {
|
|
29
35
|
const val = result[argDef.name];
|
|
@@ -72,20 +78,20 @@ export function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: Record<string, any
|
|
|
72
78
|
async function runCommand(
|
|
73
79
|
cmd: CliCommand,
|
|
74
80
|
page: IPage | null,
|
|
75
|
-
kwargs:
|
|
81
|
+
kwargs: CommandArgs,
|
|
76
82
|
debug: boolean,
|
|
77
|
-
): Promise<
|
|
83
|
+
): Promise<unknown> {
|
|
78
84
|
// Lazy-load TS module on first execution (manifest fast-path)
|
|
79
85
|
const internal = cmd as InternalCliCommand;
|
|
80
86
|
if (internal._lazy && internal._modulePath) {
|
|
81
87
|
const modulePath = internal._modulePath;
|
|
82
88
|
if (!_loadedModules.has(modulePath)) {
|
|
83
89
|
try {
|
|
84
|
-
await import(
|
|
90
|
+
await import(pathToFileURL(modulePath).href);
|
|
85
91
|
_loadedModules.add(modulePath);
|
|
86
|
-
} catch (err
|
|
92
|
+
} catch (err) {
|
|
87
93
|
throw new AdapterLoadError(
|
|
88
|
-
`Failed to load adapter module ${modulePath}: ${err
|
|
94
|
+
`Failed to load adapter module ${modulePath}: ${getErrorMessage(err)}`,
|
|
89
95
|
'Check that the adapter file exists and has no syntax errors.',
|
|
90
96
|
);
|
|
91
97
|
}
|
|
@@ -101,6 +107,24 @@ async function runCommand(
|
|
|
101
107
|
throw new Error(`Command ${fullName(cmd)} has no func or pipeline`);
|
|
102
108
|
}
|
|
103
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Resolve the pre-navigation URL for a command, or null to skip.
|
|
112
|
+
*
|
|
113
|
+
* COOKIE/HEADER strategies need the browser on the target domain so
|
|
114
|
+
* `fetch(url, { credentials: 'include' })` carries cookies.
|
|
115
|
+
* Adapters that handle their own navigation set `navigateBefore: false`.
|
|
116
|
+
*/
|
|
117
|
+
function resolvePreNav(cmd: CliCommand): string | null {
|
|
118
|
+
if (cmd.navigateBefore === false) return null;
|
|
119
|
+
if (typeof cmd.navigateBefore === 'string') return cmd.navigateBefore;
|
|
120
|
+
|
|
121
|
+
// Default: pre-navigate for COOKIE/HEADER strategies with a domain
|
|
122
|
+
if ((cmd.strategy === Strategy.COOKIE || cmd.strategy === Strategy.HEADER) && cmd.domain) {
|
|
123
|
+
return `https://${cmd.domain}`;
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
104
128
|
/**
|
|
105
129
|
* Execute a CLI command. Automatically manages browser sessions when needed.
|
|
106
130
|
*
|
|
@@ -109,22 +133,24 @@ async function runCommand(
|
|
|
109
133
|
*/
|
|
110
134
|
export async function executeCommand(
|
|
111
135
|
cmd: CliCommand,
|
|
112
|
-
rawKwargs:
|
|
136
|
+
rawKwargs: CommandArgs,
|
|
113
137
|
debug: boolean = false,
|
|
114
|
-
): Promise<
|
|
115
|
-
let kwargs:
|
|
138
|
+
): Promise<unknown> {
|
|
139
|
+
let kwargs: CommandArgs;
|
|
116
140
|
try {
|
|
117
141
|
kwargs = coerceAndValidateArgs(cmd.args, rawKwargs);
|
|
118
|
-
} catch (err
|
|
119
|
-
throw new Error(`[Argument Validation Error]\n${err
|
|
142
|
+
} catch (err) {
|
|
143
|
+
throw new Error(`[Argument Validation Error]\n${getErrorMessage(err)}`);
|
|
120
144
|
}
|
|
121
145
|
|
|
122
146
|
if (shouldUseBrowserSession(cmd)) {
|
|
123
147
|
const BrowserFactory = getBrowserFactory();
|
|
124
148
|
return browserSession(BrowserFactory, async (page) => {
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
149
|
+
// Pre-navigate to target domain for cookie/header context if needed.
|
|
150
|
+
// Each adapter controls this via `navigateBefore` (see CliCommand docs).
|
|
151
|
+
const preNavUrl = resolvePreNav(cmd);
|
|
152
|
+
if (preNavUrl) {
|
|
153
|
+
try { await page.goto(preNavUrl); await page.wait(2); } catch {}
|
|
128
154
|
}
|
|
129
155
|
return runWithTimeout(runCommand(cmd, page, kwargs, debug), {
|
|
130
156
|
timeout: cmd.timeoutSeconds ?? DEFAULT_BROWSER_COMMAND_TIMEOUT,
|
package/src/explore.ts
CHANGED
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
import * as fs from 'node:fs';
|
|
10
10
|
import * as path from 'node:path';
|
|
11
11
|
import { DEFAULT_BROWSER_EXPLORE_TIMEOUT, browserSession, runWithTimeout } from './runtime.js';
|
|
12
|
+
import type { IBrowserFactory } from './runtime.js';
|
|
12
13
|
import { VOLATILE_PARAMS, SEARCH_PARAMS, PAGINATION_PARAMS, LIMIT_PARAMS, FIELD_ROLES } from './constants.js';
|
|
13
14
|
import { detectFramework } from './scripts/framework.js';
|
|
14
15
|
import { discoverStores } from './scripts/store.js';
|
|
15
16
|
import { interactFuzz } from './scripts/interact.js';
|
|
17
|
+
import type { IPage } from './types.js';
|
|
16
18
|
|
|
17
19
|
// ── Site name detection ────────────────────────────────────────────────────
|
|
18
20
|
|
|
@@ -68,7 +70,61 @@ interface InferredCapability {
|
|
|
68
70
|
name: string; description: string; strategy: string; confidence: number;
|
|
69
71
|
endpoint: string; itemPath: string | null;
|
|
70
72
|
recommendedColumns: string[];
|
|
71
|
-
recommendedArgs: Array<{ name: string; type: string; required: boolean; default?:
|
|
73
|
+
recommendedArgs: Array<{ name: string; type: string; required: boolean; default?: unknown }>;
|
|
74
|
+
storeHint?: { store: string; action: string };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ExploreManifest {
|
|
78
|
+
site: string;
|
|
79
|
+
target_url: string;
|
|
80
|
+
final_url: string;
|
|
81
|
+
title: string;
|
|
82
|
+
framework: Record<string, boolean>;
|
|
83
|
+
stores: Array<{ type: DiscoveredStore['type']; id: string; actions: string[] }>;
|
|
84
|
+
top_strategy: string;
|
|
85
|
+
explored_at?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ExploreAuthSummary {
|
|
89
|
+
top_strategy: string;
|
|
90
|
+
indicators: string[];
|
|
91
|
+
framework: Record<string, boolean>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ExploreEndpointArtifact {
|
|
95
|
+
pattern: string;
|
|
96
|
+
method: string;
|
|
97
|
+
url: string;
|
|
98
|
+
status: number | null;
|
|
99
|
+
contentType: string;
|
|
100
|
+
score: number;
|
|
101
|
+
queryParams: string[];
|
|
102
|
+
itemPath: string | null;
|
|
103
|
+
itemCount: number;
|
|
104
|
+
detectedFields: Record<string, string>;
|
|
105
|
+
authIndicators: string[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface ExploreResult {
|
|
109
|
+
site: string;
|
|
110
|
+
target_url: string;
|
|
111
|
+
final_url: string;
|
|
112
|
+
title: string;
|
|
113
|
+
framework: Record<string, boolean>;
|
|
114
|
+
stores: DiscoveredStore[];
|
|
115
|
+
top_strategy: string;
|
|
116
|
+
endpoint_count: number;
|
|
117
|
+
api_endpoint_count: number;
|
|
118
|
+
capabilities: InferredCapability[];
|
|
119
|
+
auth_indicators: string[];
|
|
120
|
+
out_dir: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ExploreBundle {
|
|
124
|
+
manifest: ExploreManifest;
|
|
125
|
+
endpoints: ExploreEndpointArtifact[];
|
|
126
|
+
capabilities: InferredCapability[];
|
|
127
|
+
auth: ExploreAuthSummary;
|
|
72
128
|
}
|
|
73
129
|
|
|
74
130
|
/**
|
|
@@ -167,7 +223,11 @@ function flattenFields(obj: unknown, prefix: string, maxDepth: number): string[]
|
|
|
167
223
|
return names;
|
|
168
224
|
}
|
|
169
225
|
|
|
170
|
-
function
|
|
226
|
+
function isBooleanRecord(value: unknown): value is Record<string, boolean> {
|
|
227
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function scoreEndpoint(ep: { contentType: string; responseAnalysis: AnalyzedEndpoint['responseAnalysis']; pattern: string; status: number | null; hasSearchParam: boolean; hasPaginationParam: boolean; hasLimitParam: boolean }): number {
|
|
171
231
|
let s = 0;
|
|
172
232
|
if (ep.contentType.includes('json')) s += 10;
|
|
173
233
|
if (ep.responseAnalysis) { s += 5; s += Math.min(ep.responseAnalysis.itemCount, 10); s += Object.keys(ep.responseAnalysis.detectedFields).length * 2; }
|
|
@@ -321,7 +381,7 @@ function inferCapabilitiesFromEndpoints(
|
|
|
321
381
|
/** Write explore artifacts (manifest, endpoints, capabilities, auth, stores) to disk. */
|
|
322
382
|
async function writeExploreArtifacts(
|
|
323
383
|
targetDir: string,
|
|
324
|
-
result:
|
|
384
|
+
result: Omit<ExploreResult, 'out_dir'>,
|
|
325
385
|
analyzedEndpoints: AnalyzedEndpoint[],
|
|
326
386
|
stores: DiscoveredStore[],
|
|
327
387
|
): Promise<void> {
|
|
@@ -354,12 +414,12 @@ async function writeExploreArtifacts(
|
|
|
354
414
|
export async function exploreUrl(
|
|
355
415
|
url: string,
|
|
356
416
|
opts: {
|
|
357
|
-
BrowserFactory: new () =>
|
|
417
|
+
BrowserFactory: new () => IBrowserFactory;
|
|
358
418
|
site?: string; goal?: string; authenticated?: boolean;
|
|
359
419
|
outDir?: string; waitSeconds?: number; query?: string;
|
|
360
420
|
clickLabels?: string[]; auto?: boolean; workspace?: string;
|
|
361
421
|
},
|
|
362
|
-
): Promise<
|
|
422
|
+
): Promise<ExploreResult> {
|
|
363
423
|
const waitSeconds = opts.waitSeconds ?? 3.0;
|
|
364
424
|
const exploreTimeout = Math.max(DEFAULT_BROWSER_EXPLORE_TIMEOUT, 45.0 + waitSeconds * 8.0);
|
|
365
425
|
|
|
@@ -432,7 +492,10 @@ export async function exploreUrl(
|
|
|
432
492
|
|
|
433
493
|
// Step 6: Detect framework
|
|
434
494
|
let framework: Record<string, boolean> = {};
|
|
435
|
-
try {
|
|
495
|
+
try {
|
|
496
|
+
const fw = await page.evaluate(FRAMEWORK_DETECT_JS);
|
|
497
|
+
if (isBooleanRecord(fw)) framework = fw;
|
|
498
|
+
} catch {}
|
|
436
499
|
|
|
437
500
|
// Step 6.5: Discover stores (Pinia / Vuex)
|
|
438
501
|
let stores: DiscoveredStore[] = [];
|
|
@@ -467,7 +530,7 @@ export async function exploreUrl(
|
|
|
467
530
|
}, { workspace: opts.workspace });
|
|
468
531
|
}
|
|
469
532
|
|
|
470
|
-
export function renderExploreSummary(result:
|
|
533
|
+
export function renderExploreSummary(result: ExploreResult): string {
|
|
471
534
|
const lines = [
|
|
472
535
|
'opencli probe: OK', `Site: ${result.site}`, `URL: ${result.target_url}`,
|
|
473
536
|
`Title: ${result.title || '(none)'}`, `Strategy: ${result.top_strategy}`,
|
|
@@ -492,10 +555,15 @@ export function renderExploreSummary(result: Record<string, any>): string {
|
|
|
492
555
|
return lines.join('\n');
|
|
493
556
|
}
|
|
494
557
|
|
|
495
|
-
async function readPageMetadata(page:
|
|
558
|
+
async function readPageMetadata(page: IPage): Promise<{ url: string; title: string }> {
|
|
496
559
|
try {
|
|
497
560
|
const result = await page.evaluate(`() => ({ url: window.location.href, title: document.title || '' })`);
|
|
498
|
-
if (result && typeof result === 'object'
|
|
561
|
+
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
|
562
|
+
return {
|
|
563
|
+
url: String((result as Record<string, unknown>).url ?? ''),
|
|
564
|
+
title: String((result as Record<string, unknown>).title ?? ''),
|
|
565
|
+
};
|
|
566
|
+
}
|
|
499
567
|
} catch {}
|
|
500
568
|
return { url: '', title: '' };
|
|
501
569
|
}
|
package/src/generate.ts
CHANGED
|
@@ -9,10 +9,59 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { exploreUrl } from './explore.js';
|
|
12
|
-
import {
|
|
12
|
+
import type { IBrowserFactory } from './runtime.js';
|
|
13
|
+
import { synthesizeFromExplore, type SynthesizeCandidateSummary, type SynthesizeResult } from './synthesize.js';
|
|
13
14
|
|
|
14
15
|
// TODO: implement real CLI registration (copy candidate YAML to user clis dir)
|
|
15
|
-
|
|
16
|
+
interface RegisterCandidatesOptions {
|
|
17
|
+
target: string;
|
|
18
|
+
builtinClis?: string;
|
|
19
|
+
userClis?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface RegisterCandidatesResult {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
count: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface GenerateCliOptions {
|
|
29
|
+
url: string;
|
|
30
|
+
BrowserFactory: new () => IBrowserFactory;
|
|
31
|
+
builtinClis?: string;
|
|
32
|
+
userClis?: string;
|
|
33
|
+
goal?: string | null;
|
|
34
|
+
site?: string;
|
|
35
|
+
waitSeconds?: number;
|
|
36
|
+
top?: number;
|
|
37
|
+
register?: boolean;
|
|
38
|
+
workspace?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface GenerateCliResult {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
goal?: string | null;
|
|
44
|
+
normalized_goal?: string | null;
|
|
45
|
+
site: string;
|
|
46
|
+
selected_candidate: SynthesizeCandidateSummary | null;
|
|
47
|
+
selected_command: string;
|
|
48
|
+
explore: {
|
|
49
|
+
endpoint_count: number;
|
|
50
|
+
api_endpoint_count: number;
|
|
51
|
+
capability_count: number;
|
|
52
|
+
top_strategy: string;
|
|
53
|
+
framework: Record<string, boolean>;
|
|
54
|
+
};
|
|
55
|
+
synthesize: {
|
|
56
|
+
candidate_count: number;
|
|
57
|
+
candidates: Array<Pick<SynthesizeCandidateSummary, 'name' | 'strategy' | 'confidence'>>;
|
|
58
|
+
};
|
|
59
|
+
register: RegisterCandidatesResult | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function registerCandidates(_opts: RegisterCandidatesOptions): RegisterCandidatesResult {
|
|
63
|
+
return { ok: true, count: 0 };
|
|
64
|
+
}
|
|
16
65
|
|
|
17
66
|
const CAPABILITY_ALIASES: Record<string, string[]> = {
|
|
18
67
|
search: ['search', '搜索', '查找', 'query', 'keyword'],
|
|
@@ -41,7 +90,7 @@ function normalizeGoal(goal?: string | null): string | null {
|
|
|
41
90
|
/**
|
|
42
91
|
* Select the best candidate matching the user's goal.
|
|
43
92
|
*/
|
|
44
|
-
function selectCandidate(candidates:
|
|
93
|
+
function selectCandidate(candidates: SynthesizeResult['candidates'], goal?: string | null): SynthesizeCandidateSummary | null {
|
|
45
94
|
if (!candidates.length) return null;
|
|
46
95
|
if (!goal) return candidates[0]; // highest confidence first
|
|
47
96
|
|
|
@@ -58,12 +107,12 @@ function selectCandidate(candidates: any[], goal?: string | null): any {
|
|
|
58
107
|
return partial ?? candidates[0];
|
|
59
108
|
}
|
|
60
109
|
|
|
61
|
-
export async function generateCliFromUrl(opts:
|
|
110
|
+
export async function generateCliFromUrl(opts: GenerateCliOptions): Promise<GenerateCliResult> {
|
|
62
111
|
// Step 1: Deep Explore
|
|
63
112
|
const exploreResult = await exploreUrl(opts.url, {
|
|
64
113
|
BrowserFactory: opts.BrowserFactory,
|
|
65
114
|
site: opts.site,
|
|
66
|
-
goal: normalizeGoal(opts.goal) ?? opts.goal,
|
|
115
|
+
goal: normalizeGoal(opts.goal) ?? opts.goal ?? undefined,
|
|
67
116
|
waitSeconds: opts.waitSeconds ?? 3,
|
|
68
117
|
workspace: opts.workspace,
|
|
69
118
|
});
|
|
@@ -75,10 +124,10 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
|
|
|
75
124
|
|
|
76
125
|
// Step 3: Select best candidate for goal
|
|
77
126
|
const selected = selectCandidate(synthesizeResult.candidates ?? [], opts.goal);
|
|
78
|
-
const selectedSite =
|
|
127
|
+
const selectedSite = synthesizeResult.site ?? exploreResult.site;
|
|
79
128
|
|
|
80
129
|
// Step 4: Register (if requested)
|
|
81
|
-
let registerResult:
|
|
130
|
+
let registerResult: RegisterCandidatesResult | null = null;
|
|
82
131
|
if (opts.register !== false && synthesizeResult.candidate_count > 0) {
|
|
83
132
|
try {
|
|
84
133
|
registerResult = registerCandidates({
|
|
@@ -108,7 +157,7 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
|
|
|
108
157
|
},
|
|
109
158
|
synthesize: {
|
|
110
159
|
candidate_count: synthesizeResult.candidate_count,
|
|
111
|
-
candidates: (synthesizeResult.candidates ?? []).map((c
|
|
160
|
+
candidates: (synthesizeResult.candidates ?? []).map((c) => ({
|
|
112
161
|
name: c.name,
|
|
113
162
|
strategy: c.strategy,
|
|
114
163
|
confidence: c.confidence,
|
|
@@ -118,7 +167,7 @@ export async function generateCliFromUrl(opts: any): Promise<any> {
|
|
|
118
167
|
};
|
|
119
168
|
}
|
|
120
169
|
|
|
121
|
-
export function renderGenerateSummary(r:
|
|
170
|
+
export function renderGenerateSummary(r: GenerateCliResult): string {
|
|
122
171
|
const lines = [
|
|
123
172
|
`opencli generate: ${r.ok ? 'OK' : 'FAIL'}`,
|
|
124
173
|
`Site: ${r.site}`,
|
package/src/main.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import * as os from 'node:os';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
-
import { discoverClis } from './discovery.js';
|
|
9
|
+
import { discoverClis, discoverPlugins } from './discovery.js';
|
|
10
10
|
import { getCompletions } from './completion.js';
|
|
11
11
|
import { runCli } from './cli.js';
|
|
12
12
|
|
|
@@ -16,6 +16,7 @@ const BUILTIN_CLIS = path.resolve(__dirname, 'clis');
|
|
|
16
16
|
const USER_CLIS = path.join(os.homedir(), '.opencli', 'clis');
|
|
17
17
|
|
|
18
18
|
await discoverClis(BUILTIN_CLIS, USER_CLIS);
|
|
19
|
+
await discoverPlugins();
|
|
19
20
|
|
|
20
21
|
// ── Fast-path: handle --get-completions before commander parses ─────────
|
|
21
22
|
// Usage: opencli --get-completions --cursor <N> [word1 word2 ...]
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, it, expect, vi } from 'vitest';
|
|
6
6
|
import { executePipeline } from './index.js';
|
|
7
|
+
import { ConfigError } from '../errors.js';
|
|
7
8
|
import type { IPage } from '../types.js';
|
|
8
9
|
|
|
9
10
|
/** Create a minimal mock page for testing */
|
|
@@ -16,6 +17,7 @@ function createMockPage(overrides: Partial<IPage> = {}): IPage {
|
|
|
16
17
|
click: vi.fn(),
|
|
17
18
|
typeText: vi.fn(),
|
|
18
19
|
pressKey: vi.fn(),
|
|
20
|
+
getFormState: vi.fn().mockResolvedValue({}),
|
|
19
21
|
wait: vi.fn(),
|
|
20
22
|
tabs: vi.fn().mockResolvedValue([]),
|
|
21
23
|
closeTab: vi.fn(),
|
|
@@ -24,6 +26,7 @@ function createMockPage(overrides: Partial<IPage> = {}): IPage {
|
|
|
24
26
|
networkRequests: vi.fn().mockResolvedValue([]),
|
|
25
27
|
consoleMessages: vi.fn().mockResolvedValue(''),
|
|
26
28
|
scroll: vi.fn(),
|
|
29
|
+
scrollTo: vi.fn(),
|
|
27
30
|
autoScroll: vi.fn(),
|
|
28
31
|
installInterceptor: vi.fn(),
|
|
29
32
|
getInterceptedRequests: vi.fn().mockResolvedValue([]),
|
|
@@ -79,6 +82,32 @@ describe('executePipeline', () => {
|
|
|
79
82
|
]);
|
|
80
83
|
});
|
|
81
84
|
|
|
85
|
+
it('runs inline select inside map step', async () => {
|
|
86
|
+
const page = createMockPage({
|
|
87
|
+
evaluate: vi.fn().mockResolvedValue({
|
|
88
|
+
posts: [
|
|
89
|
+
{ title: 'First', rank: 1 },
|
|
90
|
+
{ title: 'Second', rank: 2 },
|
|
91
|
+
],
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
const result = await executePipeline(page, [
|
|
95
|
+
{ evaluate: 'test' },
|
|
96
|
+
{
|
|
97
|
+
map: {
|
|
98
|
+
select: 'posts',
|
|
99
|
+
title: '${{ item.title }}',
|
|
100
|
+
rank: '${{ item.rank }}',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
expect(result).toEqual([
|
|
106
|
+
{ title: 'First', rank: 1 },
|
|
107
|
+
{ title: 'Second', rank: 2 },
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
|
|
82
111
|
it('executes limit step', async () => {
|
|
83
112
|
const page = createMockPage({
|
|
84
113
|
evaluate: vi.fn().mockResolvedValue([1, 2, 3, 4, 5]),
|
|
@@ -120,13 +149,13 @@ describe('executePipeline', () => {
|
|
|
120
149
|
expect(page.wait).toHaveBeenCalledWith(2);
|
|
121
150
|
});
|
|
122
151
|
|
|
123
|
-
it('
|
|
124
|
-
|
|
125
|
-
|
|
152
|
+
it('fails fast on unknown steps', async () => {
|
|
153
|
+
await expect(executePipeline(null, [
|
|
154
|
+
{ unknownStep: 'test' },
|
|
155
|
+
], { debug: true })).rejects.toBeInstanceOf(ConfigError);
|
|
156
|
+
await expect(executePipeline(null, [
|
|
126
157
|
{ unknownStep: 'test' },
|
|
127
|
-
], { debug: true });
|
|
128
|
-
expect(stderr).toHaveBeenCalledWith(expect.stringContaining('Unknown step'));
|
|
129
|
-
stderr.mockRestore();
|
|
158
|
+
], { debug: true })).rejects.toThrow('Unknown pipeline step "unknownStep"');
|
|
130
159
|
});
|
|
131
160
|
|
|
132
161
|
it('passes args through template rendering', async () => {
|
package/src/pipeline/executor.ts
CHANGED
|
@@ -2,46 +2,95 @@
|
|
|
2
2
|
* Pipeline executor: runs YAML pipeline steps sequentially.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import type { IPage } from '../types.js';
|
|
7
7
|
import { getStep, type StepHandler } from './registry.js';
|
|
8
8
|
import { log } from '../logger.js';
|
|
9
|
+
import { ConfigError } from '../errors.js';
|
|
9
10
|
|
|
10
11
|
export interface PipelineContext {
|
|
11
|
-
args?: Record<string,
|
|
12
|
+
args?: Record<string, unknown>;
|
|
12
13
|
debug?: boolean;
|
|
14
|
+
/** Max retry attempts per step (default: 2 for browser steps, 0 for others) */
|
|
15
|
+
stepRetries?: number;
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
/** Steps that interact with the browser and may fail transiently */
|
|
19
|
+
const BROWSER_STEPS = new Set(['navigate', 'evaluate', 'click', 'type', 'press', 'wait', 'snapshot', 'scroll']);
|
|
20
|
+
|
|
15
21
|
export async function executePipeline(
|
|
16
22
|
page: IPage | null,
|
|
17
|
-
pipeline:
|
|
23
|
+
pipeline: unknown[],
|
|
18
24
|
ctx: PipelineContext = {},
|
|
19
|
-
): Promise<
|
|
25
|
+
): Promise<unknown> {
|
|
20
26
|
const args = ctx.args ?? {};
|
|
21
27
|
const debug = ctx.debug ?? false;
|
|
22
|
-
let data:
|
|
28
|
+
let data: unknown = null;
|
|
23
29
|
const total = pipeline.length;
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
try {
|
|
32
|
+
for (let i = 0; i < pipeline.length; i++) {
|
|
33
|
+
const step = pipeline[i];
|
|
34
|
+
if (!step || typeof step !== 'object') continue;
|
|
35
|
+
for (const [op, params] of Object.entries(step)) {
|
|
36
|
+
if (debug) debugStepStart(i + 1, total, op, params);
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
const handler = getStep(op);
|
|
39
|
+
if (handler) {
|
|
40
|
+
data = await executeStepWithRetry(handler, page, params, data, args, op, ctx.stepRetries);
|
|
41
|
+
} else {
|
|
42
|
+
throw new ConfigError(
|
|
43
|
+
`Unknown pipeline step "${op}" at index ${i}.`,
|
|
44
|
+
'Check the YAML pipeline step name or register the custom step before execution.',
|
|
45
|
+
);
|
|
46
|
+
}
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
if (debug) debugStepResult(op, data);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
// Attempt cleanup: close automation window on pipeline failure
|
|
53
|
+
if (page && typeof (page as unknown as Record<string, unknown>).closeWindow === 'function') {
|
|
54
|
+
try { await (page as unknown as { closeWindow: () => Promise<void> }).closeWindow(); } catch { /* ignore */ }
|
|
39
55
|
}
|
|
56
|
+
throw err;
|
|
40
57
|
}
|
|
41
58
|
return data;
|
|
42
59
|
}
|
|
43
60
|
|
|
44
|
-
function
|
|
61
|
+
async function executeStepWithRetry(
|
|
62
|
+
handler: StepHandler,
|
|
63
|
+
page: IPage | null,
|
|
64
|
+
params: unknown,
|
|
65
|
+
data: unknown,
|
|
66
|
+
args: Record<string, unknown>,
|
|
67
|
+
op: string,
|
|
68
|
+
configRetries?: number,
|
|
69
|
+
): Promise<unknown> {
|
|
70
|
+
const maxRetries = configRetries ?? (BROWSER_STEPS.has(op) ? 2 : 0);
|
|
71
|
+
|
|
72
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
73
|
+
try {
|
|
74
|
+
return await handler(page, params, data, args);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (attempt >= maxRetries) throw err;
|
|
77
|
+
// Only retry on transient browser errors
|
|
78
|
+
const msg = err instanceof Error ? err.message : '';
|
|
79
|
+
const isTransient = msg.includes('Extension disconnected')
|
|
80
|
+
|| msg.includes('attach failed')
|
|
81
|
+
|| msg.includes('no longer exists')
|
|
82
|
+
|| msg.includes('CDP connection')
|
|
83
|
+
|| msg.includes('Daemon command failed');
|
|
84
|
+
if (!isTransient) throw err;
|
|
85
|
+
// Brief delay before retry
|
|
86
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Unreachable
|
|
90
|
+
throw new Error(`Step "${op}" failed after ${maxRetries} retries`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function debugStepStart(stepNum: number, total: number, op: string, params: unknown): void {
|
|
45
94
|
let preview = '';
|
|
46
95
|
if (typeof params === 'string') {
|
|
47
96
|
preview = params.length <= 80 ? ` → ${params}` : ` → ${params.slice(0, 77)}...`;
|
|
@@ -51,7 +100,7 @@ function debugStepStart(stepNum: number, total: number, op: string, params: any)
|
|
|
51
100
|
log.step(stepNum, total, op, preview);
|
|
52
101
|
}
|
|
53
102
|
|
|
54
|
-
function debugStepResult(op: string, data:
|
|
103
|
+
function debugStepResult(op: string, data: unknown): void {
|
|
55
104
|
if (data === null || data === undefined) {
|
|
56
105
|
log.stepResult('(no data)');
|
|
57
106
|
} else if (Array.isArray(data)) {
|
package/src/pipeline/registry.ts
CHANGED
|
@@ -18,11 +18,11 @@ import { stepDownload } from './steps/download.js';
|
|
|
18
18
|
* TData is the type of the `data` state flowing into the step.
|
|
19
19
|
* TResult is the expected return type.
|
|
20
20
|
*/
|
|
21
|
-
export type StepHandler<TData =
|
|
21
|
+
export type StepHandler<TData = unknown, TResult = unknown, TParams = unknown> = (
|
|
22
22
|
page: IPage | null,
|
|
23
|
-
params:
|
|
23
|
+
params: TParams,
|
|
24
24
|
data: TData,
|
|
25
|
-
args: Record<string,
|
|
25
|
+
args: Record<string, unknown>
|
|
26
26
|
) => Promise<TResult>;
|
|
27
27
|
|
|
28
28
|
const _stepRegistry = new Map<string, StepHandler>();
|