@jackwener/opencli 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/pull_request_template.md +3 -1
- package/.github/workflows/build-extension.yml +7 -1
- package/.github/workflows/ci.yml +29 -3
- package/.github/workflows/docs.yml +1 -1
- package/.github/workflows/e2e-headed.yml +20 -0
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/security.yml +0 -3
- package/CHANGELOG.md +55 -0
- package/CONTRIBUTING.md +6 -3
- package/README.md +37 -10
- package/README.zh-CN.md +37 -10
- package/SKILL.md +7 -2
- package/TESTING.md +1 -0
- package/chatwise-opencli.ps1 +82 -0
- package/dist/analysis.d.ts +38 -0
- package/dist/analysis.js +166 -0
- package/dist/browser/cdp.d.ts +0 -4
- package/dist/browser/cdp.js +59 -38
- package/dist/browser/cdp.test.d.ts +1 -0
- package/dist/browser/cdp.test.js +52 -0
- package/dist/browser/daemon-client.js +2 -1
- package/dist/browser/discover.js +2 -1
- package/dist/browser/dom-snapshot.d.ts +2 -2
- package/dist/browser/dom-snapshot.js +54 -1
- package/dist/browser/dom-snapshot.test.js +36 -0
- package/dist/browser/errors.js +2 -1
- package/dist/browser/index.d.ts +3 -2
- package/dist/browser/index.js +2 -1
- package/dist/browser/mcp.d.ts +0 -2
- package/dist/browser/mcp.js +2 -3
- package/dist/browser/page.d.ts +4 -3
- package/dist/browser/page.js +44 -35
- package/dist/browser/stealth.d.ts +16 -0
- package/dist/browser/stealth.js +155 -0
- package/dist/browser.test.js +47 -1
- package/dist/build-manifest.js +15 -9
- package/dist/build-manifest.test.js +12 -0
- package/dist/cascade.js +4 -2
- package/dist/cli-manifest.json +639 -258
- package/dist/cli.js +57 -29
- package/dist/clis/_shared/desktop-commands.d.ts +22 -0
- package/dist/clis/_shared/desktop-commands.js +108 -0
- package/dist/clis/antigravity/serve.js +5 -2
- package/dist/clis/arxiv/search.js +1 -1
- package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
- package/dist/clis/bilibili/dynamic.test.js +68 -0
- package/dist/clis/bilibili/favorite.js +4 -2
- package/dist/clis/bilibili/following.js +3 -2
- package/dist/clis/bilibili/subtitle.js +8 -7
- package/dist/clis/bilibili/utils.js +2 -2
- package/dist/clis/boss/batchgreet.js +1 -1
- package/dist/clis/boss/chatlist.js +1 -1
- package/dist/clis/boss/chatmsg.js +1 -1
- package/dist/clis/boss/detail.js +1 -1
- package/dist/clis/boss/exchange.js +1 -1
- package/dist/clis/boss/greet.js +1 -1
- package/dist/clis/boss/invite.js +1 -1
- package/dist/clis/boss/joblist.js +1 -1
- package/dist/clis/boss/mark.js +4 -3
- package/dist/clis/boss/recommend.js +1 -1
- package/dist/clis/boss/resume.js +1 -1
- package/dist/clis/boss/search.js +1 -1
- package/dist/clis/boss/send.js +5 -4
- package/dist/clis/boss/stats.js +1 -1
- package/dist/clis/chatgpt/ask.js +4 -0
- package/dist/clis/chatgpt/new.js +5 -1
- package/dist/clis/chatgpt/read.js +5 -1
- package/dist/clis/chatgpt/send.js +2 -1
- package/dist/clis/chatgpt/status.js +5 -1
- package/dist/clis/chatwise/ask.js +8 -2
- package/dist/clis/chatwise/export.js +2 -0
- package/dist/clis/chatwise/history.js +2 -0
- package/dist/clis/chatwise/model.js +8 -3
- package/dist/clis/chatwise/new.js +3 -18
- package/dist/clis/chatwise/read.js +2 -0
- package/dist/clis/chatwise/screenshot.js +3 -27
- package/dist/clis/chatwise/send.js +8 -2
- package/dist/clis/chatwise/shared.d.ts +2 -0
- package/dist/clis/chatwise/shared.js +6 -0
- package/dist/clis/chatwise/status.js +3 -22
- package/dist/clis/codex/ask.js +6 -2
- package/dist/clis/codex/dump.js +2 -25
- package/dist/clis/codex/new.js +2 -25
- package/dist/clis/codex/screenshot.js +2 -27
- package/dist/clis/codex/send.js +6 -4
- package/dist/clis/codex/status.js +2 -22
- package/dist/clis/cursor/ask.js +2 -1
- package/dist/clis/cursor/composer.js +2 -1
- package/dist/clis/cursor/dump.js +2 -25
- package/dist/clis/cursor/new.js +2 -18
- package/dist/clis/cursor/read.js +2 -1
- package/dist/clis/cursor/screenshot.js +1 -30
- package/dist/clis/cursor/send.js +2 -1
- package/dist/clis/cursor/status.js +2 -21
- package/dist/clis/dictionary/examples.yaml +25 -0
- package/dist/clis/dictionary/search.yaml +27 -0
- package/dist/clis/dictionary/synonyms.yaml +25 -0
- package/dist/clis/douban/book-hot.js +1 -1
- package/dist/clis/douban/movie-hot.js +1 -1
- package/dist/clis/douban/search.js +1 -1
- package/dist/clis/douban/utils.d.ts +4 -1
- package/dist/clis/douban/utils.js +156 -1
- package/dist/clis/doubao/ask.js +1 -1
- package/dist/clis/doubao/new.js +1 -1
- package/dist/clis/doubao/read.js +1 -1
- package/dist/clis/doubao/send.js +1 -1
- package/dist/clis/doubao/status.js +1 -1
- package/dist/clis/doubao-app/ask.js +1 -1
- package/dist/clis/doubao-app/new.js +1 -1
- package/dist/clis/doubao-app/read.js +1 -1
- package/dist/clis/doubao-app/send.js +1 -1
- package/dist/clis/grok/ask.d.ts +4 -0
- package/dist/clis/grok/ask.js +28 -10
- package/dist/clis/grok/ask.test.js +18 -0
- package/dist/clis/jd/item.d.ts +1 -0
- package/dist/clis/jd/item.js +96 -0
- package/dist/clis/jd/item.test.d.ts +1 -0
- package/dist/clis/jd/item.test.js +28 -0
- package/dist/clis/jike/feed.js +1 -1
- package/dist/clis/jike/search.js +1 -1
- package/dist/clis/linkedin/search.js +5 -4
- package/dist/clis/linkedin/timeline.d.ts +21 -0
- package/dist/clis/linkedin/timeline.js +503 -0
- package/dist/clis/linkedin/timeline.test.d.ts +1 -0
- package/dist/clis/linkedin/timeline.test.js +81 -0
- package/dist/clis/medium/feed.js +1 -1
- package/dist/clis/medium/search.js +1 -1
- package/dist/clis/medium/user.js +1 -1
- package/dist/clis/medium/{shared.js → utils.js} +2 -1
- package/dist/clis/pixiv/detail.yaml +49 -0
- package/dist/clis/pixiv/download.d.ts +7 -0
- package/dist/clis/pixiv/download.js +78 -0
- package/dist/clis/pixiv/download.test.d.ts +1 -0
- package/dist/clis/pixiv/download.test.js +87 -0
- package/dist/clis/pixiv/illusts.d.ts +8 -0
- package/dist/clis/pixiv/illusts.js +65 -0
- package/dist/clis/pixiv/illusts.test.d.ts +1 -0
- package/dist/clis/pixiv/illusts.test.js +99 -0
- package/dist/clis/pixiv/ranking.yaml +53 -0
- package/dist/clis/pixiv/search.d.ts +6 -0
- package/dist/clis/pixiv/search.js +43 -0
- package/dist/clis/pixiv/search.test.d.ts +1 -0
- package/dist/clis/pixiv/search.test.js +83 -0
- package/dist/clis/pixiv/test-utils.d.ts +12 -0
- package/dist/clis/pixiv/test-utils.js +23 -0
- package/dist/clis/pixiv/user.yaml +46 -0
- package/dist/clis/pixiv/utils.d.ts +27 -0
- package/dist/clis/pixiv/utils.js +49 -0
- package/dist/clis/reddit/comment.js +2 -1
- package/dist/clis/reddit/read.js +4 -3
- package/dist/clis/reddit/read.test.d.ts +1 -0
- package/dist/clis/reddit/read.test.js +28 -0
- package/dist/clis/reddit/save.js +2 -1
- package/dist/clis/reddit/saved.js +7 -3
- package/dist/clis/reddit/subscribe.js +2 -1
- package/dist/clis/reddit/upvote.js +2 -1
- package/dist/clis/reddit/upvoted.js +7 -3
- package/dist/clis/sinablog/article.js +1 -1
- package/dist/clis/sinablog/hot.js +1 -1
- package/dist/clis/sinablog/user.js +1 -1
- package/dist/clis/substack/feed.js +1 -1
- package/dist/clis/substack/publication.js +1 -1
- package/dist/clis/substack/search.js +3 -2
- package/dist/clis/substack/{shared.js → utils.js} +3 -2
- package/dist/clis/tiktok/search.yaml +2 -1
- package/dist/clis/twitter/accept.js +2 -1
- package/dist/clis/twitter/article.js +4 -1
- package/dist/clis/twitter/block.js +2 -1
- package/dist/clis/twitter/bookmark.js +2 -1
- package/dist/clis/twitter/bookmarks.js +3 -2
- package/dist/clis/twitter/delete.js +2 -1
- package/dist/clis/twitter/follow.js +2 -1
- package/dist/clis/twitter/followers.js +3 -2
- package/dist/clis/twitter/following.js +3 -2
- package/dist/clis/twitter/hide-reply.js +2 -1
- package/dist/clis/twitter/like.js +2 -1
- package/dist/clis/twitter/notifications.js +2 -1
- package/dist/clis/twitter/post.js +2 -1
- package/dist/clis/twitter/profile.js +5 -2
- package/dist/clis/twitter/reply-dm.js +2 -1
- package/dist/clis/twitter/reply.js +2 -1
- package/dist/clis/twitter/search.js +30 -13
- package/dist/clis/twitter/search.test.d.ts +1 -0
- package/dist/clis/twitter/search.test.js +104 -0
- package/dist/clis/twitter/thread.js +2 -2
- package/dist/clis/twitter/timeline.js +3 -2
- package/dist/clis/twitter/trending.js +3 -2
- package/dist/clis/twitter/unblock.js +2 -1
- package/dist/clis/twitter/unbookmark.js +2 -1
- package/dist/clis/twitter/unfollow.js +2 -1
- package/dist/clis/v2ex/daily.js +3 -2
- package/dist/clis/v2ex/me.js +3 -2
- package/dist/clis/v2ex/notifications.js +4 -4
- package/dist/clis/web/read.d.ts +16 -0
- package/dist/clis/web/read.js +202 -0
- package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
- package/dist/clis/xueqiu/danjuan-utils.js +126 -0
- package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
- package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
- package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
- package/dist/clis/xueqiu/fund-holdings.js +28 -0
- package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
- package/dist/clis/xueqiu/fund-snapshot.js +25 -0
- package/dist/clis/youtube/transcript.js +5 -4
- package/dist/clis/youtube/video.js +3 -2
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/daemon.js +9 -4
- package/dist/discovery.js +11 -10
- package/dist/doctor.js +4 -2
- package/dist/download/index.d.ts +4 -12
- package/dist/download/index.js +33 -12
- package/dist/download/index.test.js +79 -2
- package/dist/download/media-download.js +4 -2
- package/dist/engine.test.js +76 -4
- package/dist/execution.d.ts +1 -9
- package/dist/execution.js +56 -46
- package/dist/explore.js +12 -111
- package/dist/external-clis.yaml +0 -8
- package/dist/external.js +7 -5
- package/dist/external.test.js +4 -0
- package/dist/generate.d.ts +0 -9
- package/dist/generate.js +4 -20
- package/dist/hooks.d.ts +46 -0
- package/dist/hooks.js +56 -0
- package/dist/hooks.test.d.ts +4 -0
- package/dist/hooks.test.js +92 -0
- package/dist/interceptor.js +70 -23
- package/dist/main.js +2 -0
- package/dist/output.js +12 -6
- package/dist/pipeline/executor.js +1 -1
- package/dist/pipeline/steps/browser.js +1 -3
- package/dist/pipeline/steps/download.js +42 -26
- package/dist/pipeline/steps/download.test.d.ts +1 -0
- package/dist/pipeline/steps/download.test.js +101 -0
- package/dist/pipeline/steps/fetch.js +40 -22
- package/dist/pipeline/steps/fetch.test.d.ts +1 -0
- package/dist/pipeline/steps/fetch.test.js +123 -0
- package/dist/pipeline/steps/transform.js +2 -6
- package/dist/pipeline/template.js +66 -52
- package/dist/pipeline/template.test.js +28 -0
- package/dist/pipeline/transform.test.js +18 -0
- package/dist/plugin.d.ts +40 -1
- package/dist/plugin.js +214 -17
- package/dist/plugin.test.d.ts +1 -1
- package/dist/plugin.test.js +219 -3
- package/dist/record.js +6 -98
- package/dist/registry-api.d.ts +2 -0
- package/dist/registry-api.js +1 -0
- package/dist/registry.d.ts +5 -2
- package/dist/registry.js +1 -2
- package/dist/runtime.d.ts +0 -1
- package/dist/runtime.js +14 -4
- package/dist/snapshotFormatter.d.ts +7 -14
- package/dist/snapshotFormatter.js +38 -78
- package/dist/utils.d.ts +9 -0
- package/dist/utils.js +29 -0
- package/dist/validate.js +3 -5
- package/dist/yaml-schema.d.ts +26 -0
- package/dist/yaml-schema.js +5 -0
- package/docs/.vitepress/config.mts +3 -0
- package/docs/adapters/browser/dictionary.md +27 -0
- package/docs/adapters/browser/douban.md +18 -8
- package/docs/adapters/browser/jd.md +27 -0
- package/docs/adapters/browser/linkedin.md +6 -0
- package/docs/adapters/browser/pixiv.md +92 -0
- package/docs/adapters/browser/web.md +30 -0
- package/docs/adapters/browser/wikipedia.md +0 -9
- package/docs/adapters/browser/xueqiu.md +27 -9
- package/docs/adapters/desktop/antigravity.md +0 -3
- package/docs/adapters/index.md +11 -9
- package/docs/comparison.md +125 -0
- package/docs/developer/contributing.md +21 -2
- package/docs/developer/testing.md +14 -8
- package/docs/developer/ts-adapter.md +18 -0
- package/docs/developer/yaml-adapter.md +16 -0
- package/docs/guide/plugins.md +10 -0
- package/docs/zh/guide/plugins.md +10 -0
- package/extension/dist/background.js +519 -444
- package/extension/manifest.json +1 -1
- package/extension/package.json +1 -1
- package/extension/src/background.test.ts +46 -1
- package/extension/src/background.ts +108 -33
- package/extension/src/cdp.ts +9 -9
- package/package.json +3 -2
- package/scripts/check-doc-coverage.sh +2 -0
- package/src/analysis.ts +170 -0
- package/src/browser/cdp.test.ts +66 -0
- package/src/browser/cdp.ts +64 -41
- package/src/browser/daemon-client.ts +4 -3
- package/src/browser/discover.ts +2 -1
- package/src/browser/dom-snapshot.test.ts +42 -0
- package/src/browser/dom-snapshot.ts +56 -3
- package/src/browser/errors.ts +2 -1
- package/src/browser/index.ts +3 -2
- package/src/browser/mcp.ts +2 -4
- package/src/browser/page.ts +43 -35
- package/src/browser/stealth.ts +156 -0
- package/src/browser.test.ts +51 -1
- package/src/build-manifest.test.ts +14 -0
- package/src/build-manifest.ts +13 -32
- package/src/cascade.ts +5 -3
- package/src/cli.ts +66 -34
- package/src/clis/_shared/desktop-commands.ts +121 -0
- package/src/clis/antigravity/serve.ts +6 -3
- package/src/clis/arxiv/search.ts +1 -1
- package/src/clis/bilibili/dynamic.test.ts +79 -0
- package/src/clis/bilibili/favorite.ts +5 -2
- package/src/clis/bilibili/following.ts +3 -2
- package/src/clis/bilibili/subtitle.ts +8 -7
- package/src/clis/bilibili/utils.ts +2 -2
- package/src/clis/boss/batchgreet.ts +1 -1
- package/src/clis/boss/chatlist.ts +1 -1
- package/src/clis/boss/chatmsg.ts +1 -1
- package/src/clis/boss/detail.ts +1 -1
- package/src/clis/boss/exchange.ts +1 -1
- package/src/clis/boss/greet.ts +1 -1
- package/src/clis/boss/invite.ts +1 -1
- package/src/clis/boss/joblist.ts +1 -1
- package/src/clis/boss/mark.ts +4 -3
- package/src/clis/boss/recommend.ts +1 -1
- package/src/clis/boss/resume.ts +1 -1
- package/src/clis/boss/search.ts +1 -1
- package/src/clis/boss/send.ts +5 -4
- package/src/clis/boss/stats.ts +1 -1
- package/src/clis/chatgpt/ask.ts +5 -0
- package/src/clis/chatgpt/new.ts +7 -2
- package/src/clis/chatgpt/read.ts +7 -2
- package/src/clis/chatgpt/send.ts +3 -2
- package/src/clis/chatgpt/status.ts +6 -1
- package/src/clis/chatwise/ask.ts +7 -2
- package/src/clis/chatwise/export.ts +2 -0
- package/src/clis/chatwise/history.ts +2 -0
- package/src/clis/chatwise/model.ts +7 -3
- package/src/clis/chatwise/new.ts +3 -20
- package/src/clis/chatwise/read.ts +2 -0
- package/src/clis/chatwise/screenshot.ts +3 -32
- package/src/clis/chatwise/send.ts +7 -2
- package/src/clis/chatwise/shared.ts +8 -0
- package/src/clis/chatwise/status.ts +3 -24
- package/src/clis/codex/ask.ts +5 -2
- package/src/clis/codex/dump.ts +2 -27
- package/src/clis/codex/new.ts +2 -28
- package/src/clis/codex/screenshot.ts +2 -32
- package/src/clis/codex/send.ts +5 -4
- package/src/clis/codex/status.ts +2 -24
- package/src/clis/cursor/ask.ts +2 -1
- package/src/clis/cursor/composer.ts +2 -1
- package/src/clis/cursor/dump.ts +2 -27
- package/src/clis/cursor/new.ts +2 -20
- package/src/clis/cursor/read.ts +2 -1
- package/src/clis/cursor/screenshot.ts +1 -36
- package/src/clis/cursor/send.ts +2 -1
- package/src/clis/cursor/status.ts +2 -22
- package/src/clis/dictionary/examples.yaml +25 -0
- package/src/clis/dictionary/search.yaml +27 -0
- package/src/clis/dictionary/synonyms.yaml +25 -0
- package/src/clis/douban/book-hot.ts +1 -1
- package/src/clis/douban/movie-hot.ts +1 -1
- package/src/clis/douban/search.ts +1 -1
- package/src/clis/douban/utils.ts +165 -1
- package/src/clis/doubao/ask.ts +1 -1
- package/src/clis/doubao/new.ts +1 -1
- package/src/clis/doubao/read.ts +1 -1
- package/src/clis/doubao/send.ts +1 -1
- package/src/clis/doubao/status.ts +1 -1
- package/src/clis/doubao-app/ask.ts +1 -1
- package/src/clis/doubao-app/new.ts +1 -1
- package/src/clis/doubao-app/read.ts +1 -1
- package/src/clis/doubao-app/send.ts +1 -1
- package/src/clis/grok/ask.test.ts +25 -0
- package/src/clis/grok/ask.ts +25 -12
- package/src/clis/jd/item.test.ts +35 -0
- package/src/clis/jd/item.ts +101 -0
- package/src/clis/jike/feed.ts +1 -1
- package/src/clis/jike/search.ts +1 -1
- package/src/clis/linkedin/search.ts +5 -4
- package/src/clis/linkedin/timeline.test.ts +99 -0
- package/src/clis/linkedin/timeline.ts +532 -0
- package/src/clis/medium/feed.ts +1 -1
- package/src/clis/medium/search.ts +1 -1
- package/src/clis/medium/user.ts +1 -1
- package/src/clis/medium/{shared.ts → utils.ts} +2 -1
- package/src/clis/pixiv/detail.yaml +49 -0
- package/src/clis/pixiv/download.test.ts +114 -0
- package/src/clis/pixiv/download.ts +91 -0
- package/src/clis/pixiv/illusts.test.ts +115 -0
- package/src/clis/pixiv/illusts.ts +78 -0
- package/src/clis/pixiv/ranking.yaml +53 -0
- package/src/clis/pixiv/search.test.ts +97 -0
- package/src/clis/pixiv/search.ts +53 -0
- package/src/clis/pixiv/test-utils.ts +29 -0
- package/src/clis/pixiv/user.yaml +46 -0
- package/src/clis/pixiv/utils.ts +62 -0
- package/src/clis/reddit/comment.ts +2 -1
- package/src/clis/reddit/read.test.ts +34 -0
- package/src/clis/reddit/read.ts +4 -3
- package/src/clis/reddit/save.ts +2 -1
- package/src/clis/reddit/saved.ts +6 -2
- package/src/clis/reddit/subscribe.ts +2 -1
- package/src/clis/reddit/upvote.ts +2 -1
- package/src/clis/reddit/upvoted.ts +6 -2
- package/src/clis/sinablog/article.ts +1 -1
- package/src/clis/sinablog/hot.ts +1 -1
- package/src/clis/sinablog/user.ts +1 -1
- package/src/clis/substack/feed.ts +1 -1
- package/src/clis/substack/publication.ts +1 -1
- package/src/clis/substack/search.ts +3 -2
- package/src/clis/substack/{shared.ts → utils.ts} +3 -2
- package/src/clis/tiktok/search.yaml +2 -1
- package/src/clis/twitter/accept.ts +2 -1
- package/src/clis/twitter/article.ts +3 -1
- package/src/clis/twitter/block.ts +2 -1
- package/src/clis/twitter/bookmark.ts +2 -1
- package/src/clis/twitter/bookmarks.ts +3 -2
- package/src/clis/twitter/delete.ts +2 -1
- package/src/clis/twitter/follow.ts +2 -1
- package/src/clis/twitter/followers.ts +3 -2
- package/src/clis/twitter/following.ts +3 -2
- package/src/clis/twitter/hide-reply.ts +2 -1
- package/src/clis/twitter/like.ts +2 -1
- package/src/clis/twitter/notifications.ts +2 -1
- package/src/clis/twitter/post.ts +2 -1
- package/src/clis/twitter/profile.ts +4 -2
- package/src/clis/twitter/reply-dm.ts +2 -1
- package/src/clis/twitter/reply.ts +2 -1
- package/src/clis/twitter/search.test.ts +113 -0
- package/src/clis/twitter/search.ts +38 -14
- package/src/clis/twitter/thread.ts +2 -2
- package/src/clis/twitter/timeline.ts +3 -2
- package/src/clis/twitter/trending.ts +3 -2
- package/src/clis/twitter/unblock.ts +2 -1
- package/src/clis/twitter/unbookmark.ts +2 -1
- package/src/clis/twitter/unfollow.ts +2 -1
- package/src/clis/v2ex/daily.ts +3 -2
- package/src/clis/v2ex/me.ts +3 -2
- package/src/clis/v2ex/notifications.ts +3 -4
- package/src/clis/web/read.ts +210 -0
- package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
- package/src/clis/xueqiu/danjuan-utils.ts +176 -0
- package/src/clis/xueqiu/fund-holdings.ts +32 -0
- package/src/clis/xueqiu/fund-snapshot.ts +27 -0
- package/src/clis/youtube/transcript.ts +5 -4
- package/src/clis/youtube/video.ts +3 -2
- package/src/constants.ts +3 -0
- package/src/daemon.ts +7 -5
- package/src/discovery.ts +12 -34
- package/src/doctor.ts +5 -3
- package/src/download/index.test.ts +93 -2
- package/src/download/index.ts +44 -23
- package/src/download/media-download.ts +5 -3
- package/src/engine.test.ts +84 -3
- package/src/execution.ts +62 -46
- package/src/explore.ts +21 -90
- package/src/external-clis.yaml +0 -8
- package/src/external.test.ts +9 -0
- package/src/external.ts +12 -10
- package/src/generate.ts +4 -41
- package/src/hooks.test.ts +126 -0
- package/src/hooks.ts +90 -0
- package/src/interceptor.ts +73 -23
- package/src/main.ts +2 -0
- package/src/output.ts +14 -6
- package/src/pipeline/executor.ts +1 -1
- package/src/pipeline/steps/browser.ts +1 -3
- package/src/pipeline/steps/download.test.ts +136 -0
- package/src/pipeline/steps/download.ts +47 -34
- package/src/pipeline/steps/fetch.test.ts +179 -0
- package/src/pipeline/steps/fetch.ts +39 -23
- package/src/pipeline/steps/transform.ts +2 -6
- package/src/pipeline/template.test.ts +28 -0
- package/src/pipeline/template.ts +67 -79
- package/src/pipeline/transform.test.ts +20 -0
- package/src/plugin.test.ts +251 -3
- package/src/plugin.ts +265 -21
- package/src/record.ts +12 -84
- package/src/registry-api.ts +2 -0
- package/src/registry.ts +7 -4
- package/src/runtime.ts +14 -4
- package/src/snapshotFormatter.ts +43 -121
- package/src/utils.ts +39 -0
- package/src/validate.ts +3 -6
- package/src/yaml-schema.ts +28 -0
- package/tests/e2e/browser-auth.test.ts +25 -0
- package/tests/e2e/plugin-management.test.ts +137 -0
- package/tests/e2e/public-commands.test.ts +34 -1
- package/vitest.config.ts +19 -1
- package/.github/workflows/pkg-pr-new.yml +0 -30
- package/dist/clis/douban/shared.d.ts +0 -4
- package/dist/clis/douban/shared.js +0 -155
- package/src/clis/douban/shared.ts +0 -165
- /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/boss/{common.js → utils.js} +0 -0
- /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/doubao/{common.js → utils.js} +0 -0
- /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
- /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/jike/{shared.js → utils.js} +0 -0
- /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
- /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
- /package/src/clis/boss/{common.ts → utils.ts} +0 -0
- /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
- /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
- /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
- /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
package/docs/adapters/index.md
CHANGED
|
@@ -6,17 +6,17 @@ Run `opencli list` for the live registry.
|
|
|
6
6
|
|
|
7
7
|
| Site | Commands | Mode |
|
|
8
8
|
|------|----------|------|
|
|
9
|
-
| **[twitter](/adapters/browser/twitter)** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` | 🔐 Browser |
|
|
9
|
+
| **[twitter](/adapters/browser/twitter)** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` `block` `unblock` `hide-reply` | 🔐 Browser |
|
|
10
10
|
| **[reddit](/adapters/browser/reddit)** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
|
|
11
11
|
| **[bilibili](/adapters/browser/bilibili)** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 Browser |
|
|
12
12
|
| **[zhihu](/adapters/browser/zhihu)** | `hot` `search` `question` `download` | 🔐 Browser |
|
|
13
|
-
| **[xiaohongshu](/adapters/browser/xiaohongshu)** | `search` `notifications` `feed` `
|
|
14
|
-
| **[xueqiu](/adapters/browser/xueqiu)** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 Browser |
|
|
13
|
+
| **[xiaohongshu](/adapters/browser/xiaohongshu)** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | 🔐 Browser |
|
|
14
|
+
| **[xueqiu](/adapters/browser/xueqiu)** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` | 🔐 Browser |
|
|
15
15
|
| **[youtube](/adapters/browser/youtube)** | `search` `video` `transcript` | 🔐 Browser |
|
|
16
16
|
| **[v2ex](/adapters/browser/v2ex)** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 🌐 / 🔐 |
|
|
17
17
|
| **[bloomberg](/adapters/browser/bloomberg)** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 🌐 / 🔐 |
|
|
18
18
|
| **[weibo](/adapters/browser/weibo)** | `hot` `search` | 🔐 Browser |
|
|
19
|
-
| **[linkedin](/adapters/browser/linkedin)** | `search` | 🔐 Browser |
|
|
19
|
+
| **[linkedin](/adapters/browser/linkedin)** | `search` `timeline` | 🔐 Browser |
|
|
20
20
|
| **[coupang](/adapters/browser/coupang)** | `search` `add-to-cart` | 🔐 Browser |
|
|
21
21
|
| **[boss](/adapters/browser/boss)** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | 🔐 Browser |
|
|
22
22
|
| **[ctrip](/adapters/browser/ctrip)** | `search` | 🔐 Browser |
|
|
@@ -30,12 +30,13 @@ Run `opencli list` for the live registry.
|
|
|
30
30
|
| **[grok](/adapters/browser/grok)** | `ask` | 🔐 Browser |
|
|
31
31
|
| **[doubao](/adapters/browser/doubao)** | `status` `new` `send` `read` `ask` | 🔐 Browser |
|
|
32
32
|
| **[weread](/adapters/browser/weread)** | `shelf` `search` `book` `ranking` `notebooks` `highlights` `notes` | 🔐 Browser |
|
|
33
|
-
| **[douban](/adapters/browser/douban)** | `search` `top250` `subject` `marks` `reviews` | 🔐 Browser |
|
|
33
|
+
| **[douban](/adapters/browser/douban)** | `search` `top250` `subject` `marks` `reviews` `movie-hot` `book-hot` | 🔐 Browser |
|
|
34
34
|
| **[facebook](/adapters/browser/facebook)** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 🔐 Browser |
|
|
35
35
|
| **[instagram](/adapters/browser/instagram)** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 🔐 Browser |
|
|
36
|
-
| **[medium](/adapters/browser/medium)** | `feed` `search` `user`
|
|
37
|
-
| **[sinablog](/adapters/browser/sinablog)** | `hot` `search` `article` `user`
|
|
38
|
-
| **[substack](/adapters/browser/substack)** | `feed` `search` `publication`
|
|
36
|
+
| **[medium](/adapters/browser/medium)** | `feed` `search` `user` | 🔐 Browser |
|
|
37
|
+
| **[sinablog](/adapters/browser/sinablog)** | `hot` `search` `article` `user` | 🔐 Browser |
|
|
38
|
+
| **[substack](/adapters/browser/substack)** | `feed` `search` `publication` | 🔐 Browser |
|
|
39
|
+
| **[pixiv](/adapters/browser/pixiv)** | `ranking` `search` `user` `illusts` `detail` `download` | 🔐 Browser |
|
|
39
40
|
| **[tiktok](/adapters/browser/tiktok)** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | 🔐 Browser |
|
|
40
41
|
|
|
41
42
|
## Public API Adapters
|
|
@@ -45,6 +46,7 @@ Run `opencli list` for the live registry.
|
|
|
45
46
|
| **[hackernews](/adapters/browser/hackernews)** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 🌐 Public |
|
|
46
47
|
| **[bbc](/adapters/browser/bbc)** | `news` | 🌐 Public |
|
|
47
48
|
| **[devto](/adapters/browser/devto)** | `top` `tag` `user` | 🌐 Public |
|
|
49
|
+
| **[dictionary](/adapters/browser/dictionary)** | `search` `synonyms` `examples` | 🌐 Public |
|
|
48
50
|
| **[apple-podcasts](/adapters/browser/apple-podcasts)** | `search` `episodes` `top` | 🌐 Public |
|
|
49
51
|
| **[xiaoyuzhou](/adapters/browser/xiaoyuzhou)** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
|
|
50
52
|
| **[yahoo-finance](/adapters/browser/yahoo-finance)** | `quote` | 🌐 Public |
|
|
@@ -53,7 +55,7 @@ Run `opencli list` for the live registry.
|
|
|
53
55
|
| **[hf](/adapters/browser/hf)** | `top` | 🌐 Public |
|
|
54
56
|
| **[sinafinance](/adapters/browser/sinafinance)** | `news` | 🌐 Public |
|
|
55
57
|
| **[stackoverflow](/adapters/browser/stackoverflow)** | `hot` `search` `bounties` `unanswered` | 🌐 Public |
|
|
56
|
-
| **[wikipedia](/adapters/browser/wikipedia)** | `search` `summary` | 🌐 Public |
|
|
58
|
+
| **[wikipedia](/adapters/browser/wikipedia)** | `search` `summary` `random` `trending` | 🌐 Public |
|
|
57
59
|
| **[lobsters](/adapters/browser/lobsters)** | `hot` `newest` `active` `tag` | 🌐 Public |
|
|
58
60
|
|
|
59
61
|
## Desktop Adapters
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Comparison Guide
|
|
2
|
+
|
|
3
|
+
OpenCLI occupies a specific niche in the browser automation ecosystem. This guide honestly evaluates where opencli excels, where it's a viable option, and where other tools are a better fit.
|
|
4
|
+
|
|
5
|
+
## At a Glance
|
|
6
|
+
|
|
7
|
+
| Tool | Approach | Best for |
|
|
8
|
+
|------|----------|----------|
|
|
9
|
+
| **opencli** | Pre-built adapters (YAML/TS) | Deterministic site commands, broad platform coverage, desktop apps |
|
|
10
|
+
| **Browser-Use** | LLM-driven browser control | General-purpose AI browser automation |
|
|
11
|
+
| **Crawl4AI** | Async web crawler | Large-scale data crawling |
|
|
12
|
+
| **Firecrawl** | Scraping API / self-hosted | Clean markdown extraction, managed or self-hosted infrastructure |
|
|
13
|
+
| **agent-browser** | Browser primitive CLI | Token-efficient AI agent browsing |
|
|
14
|
+
| **Stagehand** | AI browser framework | Developer-friendly browser automation |
|
|
15
|
+
| **Skyvern** | Visual AI automation | Cross-site generalized workflows |
|
|
16
|
+
|
|
17
|
+
## Scenario Comparison
|
|
18
|
+
|
|
19
|
+
### 1. Scheduled Batch Data Extraction
|
|
20
|
+
|
|
21
|
+
> "I want to pull trending posts from Bilibili/Reddit/HackerNews every hour into my pipeline."
|
|
22
|
+
|
|
23
|
+
| Tool | Fit | Notes |
|
|
24
|
+
|------|-----|-------|
|
|
25
|
+
| **opencli** | Best | One command, structured JSON output, zero runtime cost. Runs in cron/CI without tokens or API keys. |
|
|
26
|
+
| Crawl4AI | Good | Strong for large-scale crawling, but requires writing extraction logic per site. |
|
|
27
|
+
| Firecrawl | Viable | Managed service with clean output, but costs scale with volume. |
|
|
28
|
+
| Browser-Use / Stagehand | Poor | LLM inference on every run is slow, expensive, and non-deterministic for repeated tasks. |
|
|
29
|
+
|
|
30
|
+
**Why opencli wins here:** A command like `opencli bilibili hot -f json` returns the same structured schema every time, costs nothing to run, and finishes in seconds. For recurring data extraction from known sites, pre-built adapters beat LLM-driven approaches on cost, speed, and reliability.
|
|
31
|
+
|
|
32
|
+
### 2. AI Agent Site Operations
|
|
33
|
+
|
|
34
|
+
> "My AI agent needs to search Twitter, read Reddit threads, or post to Xiaohongshu."
|
|
35
|
+
|
|
36
|
+
| Tool | Fit | Notes |
|
|
37
|
+
|------|-----|-------|
|
|
38
|
+
| **opencli** | Best | Structured JSON output, fast deterministic execution, hundreds of commands ready to use. |
|
|
39
|
+
| agent-browser | Good | Token-efficient browser primitives, but requires LLM reasoning for every step. |
|
|
40
|
+
| Browser-Use | Viable | General-purpose, but each operation costs tokens and takes 10-60s. |
|
|
41
|
+
| Stagehand | Viable | Good DX, but same LLM-per-action cost model. |
|
|
42
|
+
|
|
43
|
+
**Why opencli wins here:** When your agent needs `twitter search "AI news" -f json`, a deterministic command that returns in seconds is strictly better than an LLM clicking through a webpage. The agent saves tokens for reasoning, not navigation.
|
|
44
|
+
|
|
45
|
+
### 3. Authenticated Operations (Login-Required Sites)
|
|
46
|
+
|
|
47
|
+
> "I need to access my bookmarks, post content, or interact with sites that require login."
|
|
48
|
+
|
|
49
|
+
| Tool | Fit | Notes |
|
|
50
|
+
|------|-----|-------|
|
|
51
|
+
| **opencli** | Best | Reuses your Chrome login session via Browser Bridge. No credentials stored or transmitted. |
|
|
52
|
+
| Browser-Use | Viable | Can use browser profiles, but credential management is manual. |
|
|
53
|
+
| Firecrawl | Poor | Cloud service cannot access your authenticated sessions. |
|
|
54
|
+
| Crawl4AI | Poor | Requires manual cookie/session injection. |
|
|
55
|
+
|
|
56
|
+
**Why opencli wins here:** The Browser Bridge extension reuses your existing Chrome login state in real-time. You log in once in Chrome, and opencli commands work immediately. No OAuth setup, no API keys, no credential files.
|
|
57
|
+
|
|
58
|
+
### 4. General Web Browsing & Exploration
|
|
59
|
+
|
|
60
|
+
> "I need to explore an unknown website, fill forms, or navigate complex multi-step flows."
|
|
61
|
+
|
|
62
|
+
| Tool | Fit | Notes |
|
|
63
|
+
|------|-----|-------|
|
|
64
|
+
| Browser-Use | Best | LLM-driven, handles arbitrary websites and flows. |
|
|
65
|
+
| Stagehand | Best | Clean API for `act()`, `extract()`, `observe()` on any page. |
|
|
66
|
+
| agent-browser | Good | Token-efficient primitives for AI agents. |
|
|
67
|
+
| Skyvern | Good | Visual AI that generalizes across sites. |
|
|
68
|
+
| **opencli** | Poor | Only works with sites that have pre-built adapters. Cannot handle arbitrary websites. |
|
|
69
|
+
|
|
70
|
+
**opencli is not the right tool here.** If you need to explore unknown websites or handle one-off tasks on sites without adapters, use an LLM-driven browser tool. opencli trades generality for determinism and cost.
|
|
71
|
+
|
|
72
|
+
### 5. Desktop App Control
|
|
73
|
+
|
|
74
|
+
> "I want to script Cursor, ChatGPT, Notion, or other Electron apps from the terminal."
|
|
75
|
+
|
|
76
|
+
| Tool | Fit | Notes |
|
|
77
|
+
|------|-----|-------|
|
|
78
|
+
| **opencli** | Best | 8 desktop adapters via CDP + AppleScript. The only CLI tool with this capability. |
|
|
79
|
+
| All others | N/A | Browser automation tools cannot control desktop applications. |
|
|
80
|
+
|
|
81
|
+
**This is unique to opencli.** No other tool in this comparison can send a prompt to ChatGPT desktop, extract code from Cursor, or write to Notion pages via CLI.
|
|
82
|
+
|
|
83
|
+
## Key Trade-offs
|
|
84
|
+
|
|
85
|
+
### opencli's Strengths
|
|
86
|
+
|
|
87
|
+
- **Zero LLM cost** — No tokens consumed at runtime. Run 10,000 times for free.
|
|
88
|
+
- **Deterministic output** — Same command always returns the same schema. Pipeable, scriptable, CI-friendly.
|
|
89
|
+
- **Speed** — Adapter commands return in seconds, not minutes.
|
|
90
|
+
- **Broad platform coverage** — 50+ sites spanning global platforms (Reddit, HackerNews, Twitter, YouTube) and Chinese platforms (Bilibili, Zhihu, Xiaohongshu, Douban, Weibo) with adapters that understand local anti-bot patterns.
|
|
91
|
+
- **Desktop app control** — CDP adapters for Cursor, Codex, Notion, ChatGPT, Discord, and more.
|
|
92
|
+
- **Easy to extend** — Drop a `.yaml` or `.ts` adapter into the `clis/` folder for auto-registration. Contributing a new site adapter is straightforward.
|
|
93
|
+
|
|
94
|
+
### opencli's Limitations
|
|
95
|
+
|
|
96
|
+
- **Coverage requires adapters** — opencli only works with sites that have pre-built adapters. Adding a new site means writing a YAML or TypeScript adapter.
|
|
97
|
+
- **Adapter maintenance** — When a website updates its DOM or API, the corresponding adapter may need updating. The community maintains these, but breakage is possible.
|
|
98
|
+
- **Not general-purpose** — Cannot handle arbitrary websites. For unknown sites, pair opencli with a general browser tool as a fallback.
|
|
99
|
+
|
|
100
|
+
## Complementary Usage
|
|
101
|
+
|
|
102
|
+
opencli works best alongside general-purpose browser tools, not as a replacement:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
Has adapter? ──yes──▶ opencli (fast, free, deterministic)
|
|
106
|
+
│
|
|
107
|
+
no
|
|
108
|
+
│
|
|
109
|
+
▼
|
|
110
|
+
One-off task? ──yes──▶ Browser-Use / Stagehand (LLM-driven)
|
|
111
|
+
│
|
|
112
|
+
no
|
|
113
|
+
│
|
|
114
|
+
▼
|
|
115
|
+
Recurring? ──yes──▶ Write an opencli adapter, then use opencli
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Further Reading
|
|
119
|
+
|
|
120
|
+
- [Architecture Overview](./developer/architecture.md)
|
|
121
|
+
- [Writing a YAML Adapter](./developer/yaml-adapter.md)
|
|
122
|
+
- [Writing a TypeScript Adapter](./developer/ts-adapter.md)
|
|
123
|
+
- [Testing Guide](./developer/testing.md)
|
|
124
|
+
- [AI Workflow](./developer/ai-workflow.md)
|
|
125
|
+
- [Contributing Guide](./developer/contributing.md)
|
|
@@ -17,7 +17,8 @@ npm run build
|
|
|
17
17
|
|
|
18
18
|
# 4. Run a few checks
|
|
19
19
|
npx tsc --noEmit
|
|
20
|
-
|
|
20
|
+
npm test
|
|
21
|
+
npm run test:adapter
|
|
21
22
|
|
|
22
23
|
# 5. Link globally (optional, for testing `opencli` command)
|
|
23
24
|
npm link
|
|
@@ -27,6 +28,12 @@ npm link
|
|
|
27
28
|
|
|
28
29
|
This is the most common type of contribution. Start with YAML when possible, and use TypeScript only when you need browser-side logic or multi-step flows.
|
|
29
30
|
|
|
31
|
+
Before you start:
|
|
32
|
+
|
|
33
|
+
- Prefer positional args for the command's primary subject (`search <query>`, `topic <id>`, `download <url>`). Reserve named flags for optional modifiers such as `--limit`, `--sort`, `--lang`, and `--output`.
|
|
34
|
+
- Normalize expected adapter failures to `CliError` subclasses instead of raw `Error` whenever possible. Prefer `AuthRequiredError`, `EmptyResultError`, `CommandExecutionError`, `TimeoutError`, and `ArgumentError` so the top-level CLI can render better messages and hints.
|
|
35
|
+
- If you add a new adapter or make a command newly discoverable, update the matching doc page and the user-facing indexes that expose it.
|
|
36
|
+
|
|
30
37
|
### YAML Adapter (Recommended for data-fetching commands)
|
|
31
38
|
|
|
32
39
|
Create a file like `src/clis/<site>/<command>.yaml`:
|
|
@@ -70,6 +77,7 @@ Create a file like `src/clis/<site>/<command>.ts`:
|
|
|
70
77
|
|
|
71
78
|
```typescript
|
|
72
79
|
import { cli, Strategy } from '../../registry.js';
|
|
80
|
+
import { CommandExecutionError, EmptyResultError } from '../../errors.js';
|
|
73
81
|
|
|
74
82
|
cli({
|
|
75
83
|
site: 'mysite',
|
|
@@ -86,6 +94,8 @@ cli({
|
|
|
86
94
|
func: async (page, kwargs) => {
|
|
87
95
|
const { query, limit = 10 } = kwargs;
|
|
88
96
|
// ... browser automation logic
|
|
97
|
+
if (!Array.isArray(data)) throw new CommandExecutionError('MySite returned an unexpected response');
|
|
98
|
+
if (!data.length) throw new EmptyResultError('mysite search', 'Try a different keyword');
|
|
89
99
|
return data.slice(0, Number(limit)).map((item: any) => ({
|
|
90
100
|
title: item.title,
|
|
91
101
|
url: item.url,
|
|
@@ -109,6 +119,7 @@ opencli <site> <command> -v # Verbose mode for debugging
|
|
|
109
119
|
- **ES Modules** — use `.js` extensions in imports (TypeScript output).
|
|
110
120
|
- **Naming**: `kebab-case` for files, `camelCase` for variables/functions, `PascalCase` for types/classes.
|
|
111
121
|
- **No default exports** — use named exports.
|
|
122
|
+
- **Errors** — throw `CliError` subclasses for expected adapter failures; avoid raw `Error` for normal adapter control flow.
|
|
112
123
|
|
|
113
124
|
## Commit Convention
|
|
114
125
|
|
|
@@ -129,8 +140,16 @@ chore: bump vitest to v4
|
|
|
129
140
|
3. Run the checks:
|
|
130
141
|
```bash
|
|
131
142
|
npx tsc --noEmit # Type check
|
|
132
|
-
|
|
143
|
+
npm test # Core unit tests
|
|
144
|
+
npm run test:adapter # Focused adapter tests (if adapter logic changed)
|
|
133
145
|
opencli validate # YAML validation (if applicable)
|
|
134
146
|
```
|
|
135
147
|
4. Commit using conventional commit format
|
|
136
148
|
5. Push and open a PR
|
|
149
|
+
|
|
150
|
+
If your PR adds a new adapter or changes user-facing commands, also verify:
|
|
151
|
+
|
|
152
|
+
- Adapter docs exist under `docs/adapters/`
|
|
153
|
+
- `docs/adapters/index.md` is updated for new adapters
|
|
154
|
+
- VitePress sidebar includes the new doc page
|
|
155
|
+
- `README.md` / `README.zh-CN.md` stay aligned when command discoverability changes
|
|
@@ -30,12 +30,14 @@ tests/
|
|
|
30
30
|
├── smoke/
|
|
31
31
|
│ └── api-health.test.ts # 外部 API、adapter 定义、命令注册健康检查
|
|
32
32
|
src/
|
|
33
|
-
|
|
33
|
+
├── **/*.test.ts # 核心单元测试(默认 `unit` project)
|
|
34
|
+
└── clis/{zhihu,twitter,reddit,bilibili}/**/*.test.ts # 聚焦 adapter tests
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
| 层 | 位置 | 当前文件数 | 运行方式 | 用途 |
|
|
37
38
|
|---|---|---:|---|---|
|
|
38
|
-
| 单元测试 | `src/**/*.test.ts` |
|
|
39
|
+
| 单元测试 | `src/**/*.test.ts`(排除 `src/clis/**`) | - | `npm test` | 内部模块、pipeline、runtime |
|
|
40
|
+
| Adapter 测试 | `src/clis/{zhihu,twitter,reddit,bilibili}/**/*.test.ts` | - | `npm run test:adapter` | 保留 4 个重点站点的 adapter 覆盖 |
|
|
39
41
|
| E2E 测试 | `tests/e2e/*.test.ts` | 5 | `npx vitest run tests/e2e/` | 真实 CLI 命令执行 |
|
|
40
42
|
| 烟雾测试 | `tests/smoke/*.test.ts` | 1 | `npx vitest run tests/smoke/` | 外部 API 与注册完整性 |
|
|
41
43
|
|
|
@@ -43,13 +45,13 @@ src/
|
|
|
43
45
|
|
|
44
46
|
## 当前覆盖范围
|
|
45
47
|
|
|
46
|
-
###
|
|
48
|
+
### 单元测试与 Adapter 测试
|
|
47
49
|
|
|
48
50
|
| 领域 | 文件 |
|
|
49
51
|
|---|---|
|
|
50
52
|
| 核心运行时与输出 | `src/browser.test.ts`, `src/browser/dom-snapshot.test.ts`, `src/build-manifest.test.ts`, `src/capabilityRouting.test.ts`, `src/doctor.test.ts`, `src/engine.test.ts`, `src/interceptor.test.ts`, `src/output.test.ts`, `src/plugin.test.ts`, `src/registry.test.ts`, `src/snapshotFormatter.test.ts` |
|
|
51
53
|
| pipeline 与下载 | `src/download/index.test.ts`, `src/pipeline/executor.test.ts`, `src/pipeline/template.test.ts`, `src/pipeline/transform.test.ts` |
|
|
52
|
-
|
|
|
54
|
+
| 聚焦 adapter 逻辑 | `src/clis/zhihu/download.test.ts`, `src/clis/twitter/timeline.test.ts`, `src/clis/reddit/read.test.ts`, `src/clis/bilibili/dynamic.test.ts` |
|
|
53
55
|
|
|
54
56
|
这些测试覆盖的重点包括:
|
|
55
57
|
|
|
@@ -99,8 +101,11 @@ npm run build # 编译(E2E / smoke 测试需要 dist/main.js)
|
|
|
99
101
|
### 运行命令
|
|
100
102
|
|
|
101
103
|
```bash
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
+
# 默认核心单元测试(不含大多数 adapter tests)
|
|
105
|
+
npm test
|
|
106
|
+
|
|
107
|
+
# 聚焦 adapter tests(只保留 4 个重点站点)
|
|
108
|
+
npm run test:adapter
|
|
104
109
|
|
|
105
110
|
# 全部 E2E 测试(会真实调用外部 API / 浏览器)
|
|
106
111
|
npx vitest run tests/e2e/
|
|
@@ -192,7 +197,8 @@ it('producthunt me fails gracefully without login', async () => {
|
|
|
192
197
|
| Job | 触发条件 | 内容 |
|
|
193
198
|
|---|---|---|
|
|
194
199
|
| `build` | push/PR 到 `main`,`dev` | `tsc --noEmit` + `npm run build` |
|
|
195
|
-
| `unit-test` | push/PR 到 `main`,`dev` | Node `20` 与 `22`
|
|
200
|
+
| `unit-test` | push/PR 到 `main`,`dev` | Node `20` 与 `22` 双版本运行核心 `unit` tests,按 `2` shard 并行 |
|
|
201
|
+
| `adapter-test` | push/PR 到 `main`,`dev` | Node `22` 运行聚焦的 `zhihu/twitter/reddit/bilibili` adapter tests |
|
|
196
202
|
| `smoke-test` | `schedule` 或 `workflow_dispatch` | 安装真实 Chrome,`xvfb-run` 执行 `tests/smoke/` |
|
|
197
203
|
|
|
198
204
|
### `e2e-headed.yml`
|
|
@@ -214,7 +220,7 @@ strategy:
|
|
|
214
220
|
node-version: ['20', '22']
|
|
215
221
|
shard: [1, 2]
|
|
216
222
|
steps:
|
|
217
|
-
- run:
|
|
223
|
+
- run: npm test -- --reporter=verbose --shard=${{ matrix.shard }}/2
|
|
218
224
|
```
|
|
219
225
|
:::
|
|
220
226
|
|
|
@@ -6,6 +6,7 @@ Use TypeScript adapters when you need browser-side logic, multi-step flows, DOM
|
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
8
|
import { cli, Strategy } from '../../registry.js';
|
|
9
|
+
import { CommandExecutionError, EmptyResultError } from '../../errors.js';
|
|
9
10
|
|
|
10
11
|
cli({
|
|
11
12
|
site: 'mysite',
|
|
@@ -34,6 +35,9 @@ cli({
|
|
|
34
35
|
})()
|
|
35
36
|
`);
|
|
36
37
|
|
|
38
|
+
if (!Array.isArray(data)) throw new CommandExecutionError('MySite returned an unexpected response');
|
|
39
|
+
if (!data.length) throw new EmptyResultError('mysite search', 'Try a different keyword');
|
|
40
|
+
|
|
37
41
|
return data.slice(0, Number(limit)).map((item: any) => ({
|
|
38
42
|
title: item.title,
|
|
39
43
|
url: item.url,
|
|
@@ -69,6 +73,20 @@ Contains parsed CLI arguments as key-value pairs. Always destructure with defaul
|
|
|
69
73
|
const { query, limit = 10, format = 'json' } = kwargs;
|
|
70
74
|
```
|
|
71
75
|
|
|
76
|
+
For most search/read/detail commands, the main subject should be positional (`opencli mysite search "rust"`, `opencli mysite article 123`) instead of a named flag such as `--query` or `--id`. Keep named flags for optional modifiers.
|
|
77
|
+
|
|
78
|
+
## Error Handling
|
|
79
|
+
|
|
80
|
+
Prefer throwing `CliError` subclasses from `src/errors.ts` for expected adapter failures:
|
|
81
|
+
|
|
82
|
+
- `AuthRequiredError` for missing login / cookies
|
|
83
|
+
- `EmptyResultError` for empty but valid responses
|
|
84
|
+
- `CommandExecutionError` for unexpected API or browser failures
|
|
85
|
+
- `TimeoutError` for site timeouts
|
|
86
|
+
- `ArgumentError` for invalid user input
|
|
87
|
+
|
|
88
|
+
Avoid raw `Error` for normal adapter control flow. This keeps top-level CLI output consistent and preserves hints for users.
|
|
89
|
+
|
|
72
90
|
## AI-Assisted Development
|
|
73
91
|
|
|
74
92
|
Use the AI workflow tools to accelerate adapter creation:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
YAML adapters are the recommended way to add new commands when the site offers a straightforward API. They use a declarative pipeline approach — no TypeScript required.
|
|
4
4
|
|
|
5
|
+
Use YAML only when the command stays mostly declarative. If you find yourself embedding long JavaScript expressions, many fallbacks, or multi-step browser logic, move the command to a TypeScript adapter instead of growing an opaque template blob.
|
|
6
|
+
|
|
5
7
|
## Basic Structure
|
|
6
8
|
|
|
7
9
|
::: v-pre
|
|
@@ -33,6 +35,14 @@ columns: [rank, title, score, url]
|
|
|
33
35
|
```
|
|
34
36
|
:::
|
|
35
37
|
|
|
38
|
+
For most commands, keep the primary subject positional. Good examples:
|
|
39
|
+
|
|
40
|
+
- `opencli mysite search "rust"`
|
|
41
|
+
- `opencli mysite topic 123`
|
|
42
|
+
- `opencli mysite download "https://example.com/post/1"`
|
|
43
|
+
|
|
44
|
+
Prefer named flags only for optional modifiers such as `--limit`, `--sort`, `--lang`, or `--output`.
|
|
45
|
+
|
|
36
46
|
## Pipeline Steps
|
|
37
47
|
|
|
38
48
|
### `fetch`
|
|
@@ -106,3 +116,9 @@ Use `${{ ... }}` for dynamic values:
|
|
|
106
116
|
## Real Example
|
|
107
117
|
|
|
108
118
|
See [`src/clis/hackernews/top.yaml`](https://github.com/jackwener/opencli/blob/main/src/clis/hackernews/top.yaml).
|
|
119
|
+
|
|
120
|
+
## Guardrails
|
|
121
|
+
|
|
122
|
+
- Add fallbacks for optional fields in `map` expressions when upstream payloads may be sparse.
|
|
123
|
+
- Keep template expressions short and readable. If the expression starts looking like a mini program, switch to TypeScript.
|
|
124
|
+
- If you add a new adapter, also add the matching doc page plus index/sidebar entries so `doc-coverage` stays green.
|
package/docs/guide/plugins.md
CHANGED
|
@@ -11,6 +11,12 @@ opencli plugin install github:ByteYue/opencli-plugin-github-trending
|
|
|
11
11
|
# List installed plugins
|
|
12
12
|
opencli plugin list
|
|
13
13
|
|
|
14
|
+
# Update one plugin
|
|
15
|
+
opencli plugin update github-trending
|
|
16
|
+
|
|
17
|
+
# Update all installed plugins
|
|
18
|
+
opencli plugin update --all
|
|
19
|
+
|
|
14
20
|
# Use the plugin (it's just a regular command)
|
|
15
21
|
opencli github-trending repos --limit 10
|
|
16
22
|
|
|
@@ -31,6 +37,10 @@ opencli plugin install https://github.com/user/repo
|
|
|
31
37
|
|
|
32
38
|
The repo name prefix `opencli-plugin-` is automatically stripped for the local directory name. For example, `opencli-plugin-hot-digest` becomes `hot-digest`.
|
|
33
39
|
|
|
40
|
+
## Version Tracking
|
|
41
|
+
|
|
42
|
+
OpenCLI records installed plugin versions in `~/.opencli/plugins.lock.json`. Each entry stores the plugin source, current git commit hash, install time, and last update time. `opencli plugin list` shows the short commit hash when version metadata is available.
|
|
43
|
+
|
|
34
44
|
## Creating a Plugin
|
|
35
45
|
|
|
36
46
|
### Option 1: YAML Plugin (Simplest)
|
package/docs/zh/guide/plugins.md
CHANGED
|
@@ -11,6 +11,12 @@ opencli plugin install github:ByteYue/opencli-plugin-github-trending
|
|
|
11
11
|
# 列出已安装插件
|
|
12
12
|
opencli plugin list
|
|
13
13
|
|
|
14
|
+
# 更新单个插件
|
|
15
|
+
opencli plugin update github-trending
|
|
16
|
+
|
|
17
|
+
# 更新全部已安装插件
|
|
18
|
+
opencli plugin update --all
|
|
19
|
+
|
|
14
20
|
# 使用插件(本质上就是普通 command)
|
|
15
21
|
opencli github-trending today
|
|
16
22
|
|
|
@@ -31,6 +37,10 @@ opencli plugin install https://github.com/user/repo
|
|
|
31
37
|
|
|
32
38
|
如果仓库名带 `opencli-plugin-` 前缀,本地目录会自动去掉这个前缀。例如 `opencli-plugin-hot-digest` 会变成 `hot-digest`。
|
|
33
39
|
|
|
40
|
+
## 版本追踪
|
|
41
|
+
|
|
42
|
+
OpenCLI 会把已安装 plugin 的版本记录到 `~/.opencli/plugins.lock.json`。每条记录会保存 plugin source、当前 git commit hash、安装时间,以及最近一次更新时间。只要有这份元数据,`opencli plugin list` 就会显示对应的短 commit hash。
|
|
43
|
+
|
|
34
44
|
## YAML plugin 示例
|
|
35
45
|
|
|
36
46
|
```text
|