@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/src/cli.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { PKG_VERSION } from './version.js';
|
|
|
15
15
|
import { printCompletionScript } from './completion.js';
|
|
16
16
|
import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled } from './external.js';
|
|
17
17
|
import { registerAllCommands } from './commanderAdapter.js';
|
|
18
|
+
import { getErrorMessage } from './errors.js';
|
|
18
19
|
|
|
19
20
|
export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
20
21
|
const program = new Command();
|
|
@@ -173,8 +174,6 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
173
174
|
const r = await generateCliFromUrl({
|
|
174
175
|
url,
|
|
175
176
|
BrowserFactory: getBrowserFactory(),
|
|
176
|
-
builtinClis: BUILTIN_CLIS,
|
|
177
|
-
userClis: USER_CLIS,
|
|
178
177
|
goal: opts.goal,
|
|
179
178
|
site: opts.site,
|
|
180
179
|
workspace,
|
|
@@ -262,8 +261,8 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
262
261
|
const name = installPlugin(source);
|
|
263
262
|
await discoverPlugins();
|
|
264
263
|
console.log(chalk.green(`✅ Plugin "${name}" installed successfully. Commands are ready to use.`));
|
|
265
|
-
} catch (err
|
|
266
|
-
console.error(chalk.red(`Error: ${err
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
|
|
267
266
|
process.exitCode = 1;
|
|
268
267
|
}
|
|
269
268
|
});
|
|
@@ -277,25 +276,69 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
277
276
|
try {
|
|
278
277
|
uninstallPlugin(name);
|
|
279
278
|
console.log(chalk.green(`✅ Plugin "${name}" uninstalled.`));
|
|
280
|
-
} catch (err
|
|
281
|
-
console.error(chalk.red(`Error: ${err
|
|
279
|
+
} catch (err) {
|
|
280
|
+
console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
|
|
282
281
|
process.exitCode = 1;
|
|
283
282
|
}
|
|
284
283
|
});
|
|
285
284
|
|
|
286
285
|
pluginCmd
|
|
287
286
|
.command('update')
|
|
288
|
-
.description('Update a plugin to the latest version')
|
|
289
|
-
.argument('
|
|
290
|
-
.
|
|
291
|
-
|
|
287
|
+
.description('Update a plugin (or all plugins) to the latest version')
|
|
288
|
+
.argument('[name]', 'Plugin name (required unless --all is passed)')
|
|
289
|
+
.option('--all', 'Update all installed plugins')
|
|
290
|
+
.action(async (name: string | undefined, opts: { all?: boolean }) => {
|
|
291
|
+
if (!name && !opts.all) {
|
|
292
|
+
console.error(chalk.red('Error: Please specify a plugin name or use the --all flag.'));
|
|
293
|
+
process.exitCode = 1;
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (name && opts.all) {
|
|
297
|
+
console.error(chalk.red('Error: Cannot specify both a plugin name and --all.'));
|
|
298
|
+
process.exitCode = 1;
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const { updatePlugin, updateAllPlugins } = await import('./plugin.js');
|
|
292
303
|
const { discoverPlugins } = await import('./discovery.js');
|
|
304
|
+
if (opts.all) {
|
|
305
|
+
const results = updateAllPlugins();
|
|
306
|
+
if (results.length > 0) {
|
|
307
|
+
await discoverPlugins();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let hasErrors = false;
|
|
311
|
+
console.log(chalk.bold(' Update Results:'));
|
|
312
|
+
for (const result of results) {
|
|
313
|
+
if (result.success) {
|
|
314
|
+
console.log(` ${chalk.green('✓')} ${result.name}`);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
hasErrors = true;
|
|
318
|
+
console.log(` ${chalk.red('✗')} ${result.name} — ${chalk.dim(result.error)}`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (results.length === 0) {
|
|
322
|
+
console.log(chalk.dim(' No plugins installed.'));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
console.log();
|
|
327
|
+
if (hasErrors) {
|
|
328
|
+
console.error(chalk.red('Completed with some errors.'));
|
|
329
|
+
process.exitCode = 1;
|
|
330
|
+
} else {
|
|
331
|
+
console.log(chalk.green('✅ All plugins updated successfully.'));
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
293
336
|
try {
|
|
294
|
-
updatePlugin(name);
|
|
337
|
+
updatePlugin(name!);
|
|
295
338
|
await discoverPlugins();
|
|
296
339
|
console.log(chalk.green(`✅ Plugin "${name}" updated successfully.`));
|
|
297
|
-
} catch (err
|
|
298
|
-
console.error(chalk.red(`Error: ${err
|
|
340
|
+
} catch (err) {
|
|
341
|
+
console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
|
|
299
342
|
process.exitCode = 1;
|
|
300
343
|
}
|
|
301
344
|
});
|
|
@@ -326,9 +369,10 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
326
369
|
console.log(chalk.bold(' Installed plugins'));
|
|
327
370
|
console.log();
|
|
328
371
|
for (const p of plugins) {
|
|
372
|
+
const version = p.version ? chalk.green(` @${p.version}`) : '';
|
|
329
373
|
const cmds = p.commands.length > 0 ? chalk.dim(` (${p.commands.join(', ')})`) : '';
|
|
330
374
|
const src = p.source ? chalk.dim(` ← ${p.source}`) : '';
|
|
331
|
-
console.log(` ${chalk.cyan(p.name)}${cmds}${src}`);
|
|
375
|
+
console.log(` ${chalk.cyan(p.name)}${version}${cmds}${src}`);
|
|
332
376
|
}
|
|
333
377
|
console.log();
|
|
334
378
|
console.log(chalk.dim(` ${plugins.length} plugin(s) installed`));
|
|
@@ -371,8 +415,8 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
371
415
|
})();
|
|
372
416
|
try {
|
|
373
417
|
executeExternalCli(name, args, externalClis);
|
|
374
|
-
} catch (err
|
|
375
|
-
console.error(chalk.red(`Error: ${err
|
|
418
|
+
} catch (err) {
|
|
419
|
+
console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
|
|
376
420
|
process.exitCode = 1;
|
|
377
421
|
}
|
|
378
422
|
}
|
|
@@ -408,29 +452,17 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
|
|
|
408
452
|
registerAllCommands(program, siteGroups);
|
|
409
453
|
|
|
410
454
|
// ── Unknown command fallback ──────────────────────────────────────────────
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
'rm', 'sudo', 'dd', 'mkfs', 'fdisk', 'shutdown', 'reboot',
|
|
414
|
-
'kill', 'killall', 'chmod', 'chown', 'passwd', 'su', 'mount',
|
|
415
|
-
'umount', 'format', 'diskutil',
|
|
416
|
-
]);
|
|
455
|
+
// Security: do NOT auto-discover and register arbitrary system binaries.
|
|
456
|
+
// Only explicitly registered external CLIs (via `opencli register`) are allowed.
|
|
417
457
|
|
|
418
458
|
program.on('command:*', (operands: string[]) => {
|
|
419
459
|
const binary = operands[0];
|
|
420
|
-
|
|
421
|
-
console.error(chalk.red(`Refusing to register system command '${binary}'.`));
|
|
422
|
-
process.exitCode = 1;
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
460
|
+
console.error(chalk.red(`error: unknown command '${binary}'`));
|
|
425
461
|
if (isBinaryInstalled(binary)) {
|
|
426
|
-
console.
|
|
427
|
-
registerExternalCli(binary);
|
|
428
|
-
passthroughExternal(binary);
|
|
429
|
-
} else {
|
|
430
|
-
console.error(chalk.red(`error: unknown command '${binary}'`));
|
|
431
|
-
program.outputHelp();
|
|
432
|
-
process.exitCode = 1;
|
|
462
|
+
console.error(chalk.dim(` Tip: '${binary}' exists on your PATH. Use 'opencli register ${binary}' to add it as an external CLI.`));
|
|
433
463
|
}
|
|
464
|
+
program.outputHelp();
|
|
465
|
+
process.exitCode = 1;
|
|
434
466
|
});
|
|
435
467
|
|
|
436
468
|
program.parse();
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared command factories for Electron/desktop app adapters.
|
|
3
|
+
* Eliminates duplicate screenshot/status/new/dump implementations
|
|
4
|
+
* across cursor, codex, chatwise, etc.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import { cli, Strategy } from '../../registry.js';
|
|
9
|
+
import type { IPage } from '../../types.js';
|
|
10
|
+
import type { CliOptions } from '../../registry.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Factory: capture DOM HTML + accessibility snapshot.
|
|
14
|
+
*/
|
|
15
|
+
export function makeScreenshotCommand(site: string, displayName?: string, extra: Partial<CliOptions> = {}) {
|
|
16
|
+
const label = displayName ?? site;
|
|
17
|
+
return cli({
|
|
18
|
+
...extra,
|
|
19
|
+
site,
|
|
20
|
+
name: 'screenshot',
|
|
21
|
+
description: `Capture a snapshot of the current ${label} window (DOM + Accessibility tree)`,
|
|
22
|
+
domain: 'localhost',
|
|
23
|
+
strategy: Strategy.UI,
|
|
24
|
+
browser: true,
|
|
25
|
+
args: [
|
|
26
|
+
{ name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
|
|
27
|
+
],
|
|
28
|
+
columns: ['Status', 'File'],
|
|
29
|
+
func: async (page: IPage, kwargs: any) => {
|
|
30
|
+
const outputPath = (kwargs.output as string) || `/tmp/${site}-snapshot.txt`;
|
|
31
|
+
|
|
32
|
+
const snap = await page.snapshot({ compact: true });
|
|
33
|
+
const html = await page.evaluate('document.documentElement.outerHTML');
|
|
34
|
+
|
|
35
|
+
const htmlPath = outputPath.replace(/\.\w+$/, '') + '-dom.html';
|
|
36
|
+
const snapPath = outputPath.replace(/\.\w+$/, '') + '-a11y.txt';
|
|
37
|
+
|
|
38
|
+
fs.writeFileSync(htmlPath, html);
|
|
39
|
+
fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2));
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
{ Status: 'Success', File: htmlPath },
|
|
43
|
+
{ Status: 'Success', File: snapPath },
|
|
44
|
+
];
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Factory: check CDP connection status.
|
|
51
|
+
*/
|
|
52
|
+
export function makeStatusCommand(site: string, displayName?: string, extra: Partial<CliOptions> = {}) {
|
|
53
|
+
const label = displayName ?? site;
|
|
54
|
+
return cli({
|
|
55
|
+
...extra,
|
|
56
|
+
site,
|
|
57
|
+
name: 'status',
|
|
58
|
+
description: `Check active CDP connection to ${label}`,
|
|
59
|
+
domain: 'localhost',
|
|
60
|
+
strategy: Strategy.UI,
|
|
61
|
+
browser: true,
|
|
62
|
+
columns: ['Status', 'Url', 'Title'],
|
|
63
|
+
func: async (page: IPage) => {
|
|
64
|
+
const url = await page.evaluate('window.location.href');
|
|
65
|
+
const title = await page.evaluate('document.title');
|
|
66
|
+
return [{ Status: 'Connected', Url: url, Title: title }];
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Factory: start a new session via Cmd/Ctrl+N.
|
|
73
|
+
*/
|
|
74
|
+
export function makeNewCommand(site: string, displayName?: string, extra: Partial<CliOptions> = {}) {
|
|
75
|
+
const label = displayName ?? site;
|
|
76
|
+
return cli({
|
|
77
|
+
...extra,
|
|
78
|
+
site,
|
|
79
|
+
name: 'new',
|
|
80
|
+
description: `Start a new ${label} session`,
|
|
81
|
+
domain: 'localhost',
|
|
82
|
+
strategy: Strategy.UI,
|
|
83
|
+
browser: true,
|
|
84
|
+
columns: ['Status'],
|
|
85
|
+
func: async (page: IPage) => {
|
|
86
|
+
const isMac = process.platform === 'darwin';
|
|
87
|
+
await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
|
|
88
|
+
await page.wait(1);
|
|
89
|
+
return [{ Status: 'Success' }];
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Factory: dump DOM + snapshot for reverse-engineering.
|
|
96
|
+
*/
|
|
97
|
+
export function makeDumpCommand(site: string) {
|
|
98
|
+
return cli({
|
|
99
|
+
site,
|
|
100
|
+
name: 'dump',
|
|
101
|
+
description: `Dump the DOM and Accessibility tree of ${site} for reverse-engineering`,
|
|
102
|
+
domain: 'localhost',
|
|
103
|
+
strategy: Strategy.UI,
|
|
104
|
+
browser: true,
|
|
105
|
+
columns: ['action', 'files'],
|
|
106
|
+
func: async (page: IPage) => {
|
|
107
|
+
const dom = await page.evaluate('document.body.innerHTML');
|
|
108
|
+
fs.writeFileSync(`/tmp/${site}-dom.html`, dom);
|
|
109
|
+
|
|
110
|
+
const snap = await page.snapshot({ interactive: false });
|
|
111
|
+
fs.writeFileSync(`/tmp/${site}-snapshot.json`, JSON.stringify(snap, null, 2));
|
|
112
|
+
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
action: 'Dom extraction finished',
|
|
116
|
+
files: `/tmp/${site}-dom.html, /tmp/${site}-snapshot.json`,
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
|
|
14
14
|
import { CDPBridge } from '../../browser/cdp.js';
|
|
15
15
|
import type { IPage } from '../../types.js';
|
|
16
|
+
import { getErrorMessage } from '../../errors.js';
|
|
16
17
|
|
|
17
18
|
// ─── Types ───────────────────────────────────────────────────────────
|
|
18
19
|
|
|
@@ -461,15 +462,17 @@ export async function startServe(opts: { port?: number } = {}): Promise<void> {
|
|
|
461
462
|
cdp = new CDPBridge();
|
|
462
463
|
try {
|
|
463
464
|
page = await cdp.connect({ timeout: 15_000 });
|
|
464
|
-
} catch (err:
|
|
465
|
+
} catch (err: unknown) {
|
|
465
466
|
cdp = null;
|
|
466
|
-
const
|
|
467
|
+
const errMsg = getErrorMessage(err);
|
|
468
|
+
const cause = err instanceof Error ? (err.cause as Record<string, unknown> | undefined) : undefined;
|
|
469
|
+
const isRefused = cause?.code === 'ECONNREFUSED' || errMsg.includes('ECONNREFUSED');
|
|
467
470
|
throw new Error(
|
|
468
471
|
isRefused
|
|
469
472
|
? `Cannot connect to Antigravity at ${endpoint}.\n` +
|
|
470
473
|
' 1. Make sure Antigravity is running\n' +
|
|
471
474
|
' 2. Launch with: --remote-debugging-port=9224'
|
|
472
|
-
: `CDP connection failed: ${
|
|
475
|
+
: `CDP connection failed: ${errMsg}`
|
|
473
476
|
);
|
|
474
477
|
}
|
|
475
478
|
|
package/src/clis/arxiv/search.ts
CHANGED
|
@@ -15,7 +15,7 @@ cli({
|
|
|
15
15
|
columns: ['id', 'title', 'authors', 'published'],
|
|
16
16
|
func: async (_page, args) => {
|
|
17
17
|
const limit = Math.max(1, Math.min(Number(args.limit), 25));
|
|
18
|
-
const query = encodeURIComponent(`all:${args.
|
|
18
|
+
const query = encodeURIComponent(`all:${args.query}`);
|
|
19
19
|
const xml = await arxivFetch(`search_query=${query}&max_results=${limit}&sortBy=relevance`);
|
|
20
20
|
const entries = parseEntries(xml);
|
|
21
21
|
if (!entries.length) throw new CliError('NOT_FOUND', 'No papers found', 'Try a different keyword');
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const { mockApiGet } = vi.hoisted(() => ({
|
|
4
|
+
mockApiGet: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
vi.mock('./utils.js', () => ({
|
|
8
|
+
apiGet: mockApiGet,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
import { getRegistry } from '../../registry.js';
|
|
12
|
+
import './dynamic.js';
|
|
13
|
+
|
|
14
|
+
describe('bilibili dynamic adapter', () => {
|
|
15
|
+
const command = getRegistry().get('bilibili/dynamic');
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
mockApiGet.mockReset();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('maps desc text rows from the dynamic feed payload', async () => {
|
|
22
|
+
mockApiGet.mockResolvedValue({
|
|
23
|
+
data: {
|
|
24
|
+
items: [
|
|
25
|
+
{
|
|
26
|
+
id_str: '123',
|
|
27
|
+
modules: {
|
|
28
|
+
module_author: { name: 'Alice' },
|
|
29
|
+
module_dynamic: { desc: { text: 'hello world' } },
|
|
30
|
+
module_stat: { like: { count: 9 } },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const result = await command!.func!({} as any, { limit: 5 });
|
|
38
|
+
|
|
39
|
+
expect(mockApiGet).toHaveBeenCalledWith({}, '/x/polymer/web-dynamic/v1/feed/all', { params: {}, signed: false });
|
|
40
|
+
expect(result).toEqual([
|
|
41
|
+
{
|
|
42
|
+
id: '123',
|
|
43
|
+
author: 'Alice',
|
|
44
|
+
text: 'hello world',
|
|
45
|
+
likes: 9,
|
|
46
|
+
url: 'https://t.bilibili.com/123',
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('falls back to archive title when desc text is absent', async () => {
|
|
52
|
+
mockApiGet.mockResolvedValue({
|
|
53
|
+
data: {
|
|
54
|
+
items: [
|
|
55
|
+
{
|
|
56
|
+
id_str: '456',
|
|
57
|
+
modules: {
|
|
58
|
+
module_author: { name: 'Bob' },
|
|
59
|
+
module_dynamic: { major: { archive: { title: 'Video title' } } },
|
|
60
|
+
module_stat: { like: { count: 3 } },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const result = await command!.func!({} as any, { limit: 5 });
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual([
|
|
70
|
+
{
|
|
71
|
+
id: '456',
|
|
72
|
+
author: 'Bob',
|
|
73
|
+
text: 'Video title',
|
|
74
|
+
likes: 3,
|
|
75
|
+
url: 'https://t.bilibili.com/456',
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
-
import { apiGet, payloadData } from './utils.js';
|
|
2
|
+
import { apiGet, payloadData, getSelfUid } from './utils.js';
|
|
3
3
|
|
|
4
4
|
cli({
|
|
5
5
|
site: 'bilibili',
|
|
@@ -15,9 +15,12 @@ cli({
|
|
|
15
15
|
func: async (page, kwargs) => {
|
|
16
16
|
const { limit = 20, page: pageNum = 1 } = kwargs;
|
|
17
17
|
|
|
18
|
+
// Get current user's UID
|
|
19
|
+
const uid = await getSelfUid(page);
|
|
20
|
+
|
|
18
21
|
// Get default favorite folder ID
|
|
19
22
|
const foldersPayload = await apiGet(page, '/x/v3/fav/folder/created/list-all', {
|
|
20
|
-
params: { up_mid:
|
|
23
|
+
params: { up_mid: uid },
|
|
21
24
|
signed: true,
|
|
22
25
|
});
|
|
23
26
|
const folders = payloadData(foldersPayload)?.list ?? [];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { CommandExecutionError } from '../../errors.js';
|
|
2
3
|
import type { IPage } from '../../types.js';
|
|
3
4
|
import { fetchJson, getSelfUid, resolveUid } from './utils.js';
|
|
4
5
|
|
|
@@ -14,7 +15,7 @@ cli({
|
|
|
14
15
|
],
|
|
15
16
|
columns: ['mid', 'name', 'sign', 'following', 'fans'],
|
|
16
17
|
func: async (page: IPage | null, kwargs: any) => {
|
|
17
|
-
if (!page) throw new
|
|
18
|
+
if (!page) throw new CommandExecutionError('Browser session required for bilibili following');
|
|
18
19
|
|
|
19
20
|
// 1. Resolve UID (default to self)
|
|
20
21
|
const uid = kwargs.uid
|
|
@@ -30,7 +31,7 @@ cli({
|
|
|
30
31
|
);
|
|
31
32
|
|
|
32
33
|
if (payload.code !== 0) {
|
|
33
|
-
throw new
|
|
34
|
+
throw new CommandExecutionError(`获取关注列表失败: ${payload.message} (${payload.code})`);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
const list = payload.data?.list || [];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { AuthRequiredError, CommandExecutionError, EmptyResultError, SelectorError } from '../../errors.js';
|
|
2
3
|
import type { IPage } from '../../types.js';
|
|
3
4
|
import { apiGet } from './utils.js';
|
|
4
5
|
|
|
@@ -13,7 +14,7 @@ cli({
|
|
|
13
14
|
],
|
|
14
15
|
columns: ['index', 'from', 'to', 'content'],
|
|
15
16
|
func: async (page: IPage | null, kwargs: any) => {
|
|
16
|
-
if (!page) throw new
|
|
17
|
+
if (!page) throw new CommandExecutionError('Browser session required for bilibili subtitle');
|
|
17
18
|
// 1. 先前往视频详情页 (建立有鉴权的 Session,且这里不需要加载完整个视频)
|
|
18
19
|
await page.goto(`https://www.bilibili.com/video/${kwargs.bvid}/`);
|
|
19
20
|
|
|
@@ -24,7 +25,7 @@ cli({
|
|
|
24
25
|
})()`);
|
|
25
26
|
|
|
26
27
|
if (!cid) {
|
|
27
|
-
throw new
|
|
28
|
+
throw new SelectorError('videoData.cid', '无法在页面中提取到当前视频的 CID,请检查页面是否正常加载。');
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
// 3. 在 Node 端使用 apiGet 获取带 Wbi 签名的字幕列表
|
|
@@ -35,12 +36,12 @@ cli({
|
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
if (payload.code !== 0) {
|
|
38
|
-
throw new
|
|
39
|
+
throw new CommandExecutionError(`获取视频播放信息失败: ${payload.message} (${payload.code})`);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
const subtitles = payload.data?.subtitle?.subtitles || [];
|
|
42
43
|
if (subtitles.length === 0) {
|
|
43
|
-
throw new
|
|
44
|
+
throw new EmptyResultError('bilibili subtitle', '此视频没有发现外挂或智能字幕。');
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
// 4. 选择目标字幕语言
|
|
@@ -50,7 +51,7 @@ cli({
|
|
|
50
51
|
|
|
51
52
|
const targetSubUrl = target.subtitle_url;
|
|
52
53
|
if (!targetSubUrl || targetSubUrl === '') {
|
|
53
|
-
throw new
|
|
54
|
+
throw new AuthRequiredError('bilibili.com', '[风控拦截/未登录] 获取到的 subtitle_url 为空!请确保 CLI 已成功登录且风控未封锁此账号。');
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
const finalUrl = targetSubUrl.startsWith('//') ? 'https:' + targetSubUrl : targetSubUrl;
|
|
@@ -81,12 +82,12 @@ cli({
|
|
|
81
82
|
const items = await page.evaluate(fetchJs);
|
|
82
83
|
|
|
83
84
|
if (items?.error) {
|
|
84
|
-
throw new
|
|
85
|
+
throw new CommandExecutionError(`字幕获取失败: ${items.error}${items.text ? ' — ' + items.text : ''}`);
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
const finalItems = items?.data || [];
|
|
88
89
|
if (!Array.isArray(finalItems)) {
|
|
89
|
-
throw new
|
|
90
|
+
throw new CommandExecutionError('解析到的字幕列表对象不符合数组格式');
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
// 6. 数据映射
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { IPage } from '../../types.js';
|
|
6
|
-
import { AuthRequiredError } from '../../errors.js';
|
|
6
|
+
import { AuthRequiredError, EmptyResultError } from '../../errors.js';
|
|
7
7
|
|
|
8
8
|
const MIXIN_KEY_ENC_TAB = [
|
|
9
9
|
46,47,18,2,53,8,23,32,15,50,10,31,58,3,45,35,27,43,5,49,
|
|
@@ -112,5 +112,5 @@ export async function resolveUid(page: IPage, input: string): Promise<string> {
|
|
|
112
112
|
});
|
|
113
113
|
const results = payload?.data?.result ?? [];
|
|
114
114
|
if (results.length > 0) return String(results[0].mid);
|
|
115
|
-
throw new
|
|
115
|
+
throw new EmptyResultError(`bilibili user search: ${input}`, 'User may not exist or username may have changed.');
|
|
116
116
|
}
|
package/src/clis/boss/chatmsg.ts
CHANGED
package/src/clis/boss/detail.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BOSS直聘 job detail — fetch full job posting details via browser cookie API.
|
|
3
3
|
*/
|
|
4
4
|
import { cli, Strategy } from '../../registry.js';
|
|
5
|
-
import { requirePage, navigateTo, bossFetch, verbose } from './
|
|
5
|
+
import { requirePage, navigateTo, bossFetch, verbose } from './utils.js';
|
|
6
6
|
|
|
7
7
|
cli({
|
|
8
8
|
site: 'boss',
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BOSS直聘 exchange — request phone/wechat exchange with a candidate.
|
|
3
3
|
*/
|
|
4
4
|
import { cli, Strategy } from '../../registry.js';
|
|
5
|
-
import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './
|
|
5
|
+
import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './utils.js';
|
|
6
6
|
|
|
7
7
|
cli({
|
|
8
8
|
site: 'boss',
|
package/src/clis/boss/greet.ts
CHANGED
package/src/clis/boss/invite.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BOSS直聘 invite — send interview invitation to a candidate.
|
|
3
3
|
*/
|
|
4
4
|
import { cli, Strategy } from '../../registry.js';
|
|
5
|
-
import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './
|
|
5
|
+
import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './utils.js';
|
|
6
6
|
|
|
7
7
|
cli({
|
|
8
8
|
site: 'boss',
|
package/src/clis/boss/joblist.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BOSS直聘 job list — list my published jobs via boss API.
|
|
3
3
|
*/
|
|
4
4
|
import { cli, Strategy } from '../../registry.js';
|
|
5
|
-
import { requirePage, navigateToChat, bossFetch, verbose } from './
|
|
5
|
+
import { requirePage, navigateToChat, bossFetch, verbose } from './utils.js';
|
|
6
6
|
|
|
7
7
|
cli({
|
|
8
8
|
site: 'boss',
|
package/src/clis/boss/mark.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* 6=已交换微信, 7=不合适, 8=牛人发起, 11=收藏
|
|
7
7
|
*/
|
|
8
8
|
import { cli, Strategy } from '../../registry.js';
|
|
9
|
-
import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './
|
|
9
|
+
import { requirePage, navigateToChat, bossFetch, findFriendByUid, verbose } from './utils.js';
|
|
10
|
+
import { ArgumentError, EmptyResultError } from '../../errors.js';
|
|
10
11
|
|
|
11
12
|
const LABEL_MAP: Record<string, number> = {
|
|
12
13
|
'新招呼': 1, '沟通中': 2, '已约面': 3, '已获取简历': 4,
|
|
@@ -44,7 +45,7 @@ cli({
|
|
|
44
45
|
if (entry) {
|
|
45
46
|
labelId = entry[1];
|
|
46
47
|
} else {
|
|
47
|
-
throw new
|
|
48
|
+
throw new ArgumentError(`未知标签: ${labelInput}。可用标签: ${Object.keys(LABEL_MAP).join(', ')}`);
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -53,7 +54,7 @@ cli({
|
|
|
53
54
|
await navigateToChat(page);
|
|
54
55
|
|
|
55
56
|
const friend = await findFriendByUid(page, kwargs.uid, { checkGreetList: true });
|
|
56
|
-
if (!friend) throw new
|
|
57
|
+
if (!friend) throw new EmptyResultError('boss candidate search');
|
|
57
58
|
|
|
58
59
|
const friendName = friend.name || '候选人';
|
|
59
60
|
const action = remove ? 'deleteMark' : 'addMark';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BOSS直聘 recommend — view recommended candidates (新招呼/greet sort list).
|
|
3
3
|
*/
|
|
4
4
|
import { cli, Strategy } from '../../registry.js';
|
|
5
|
-
import { requirePage, navigateToChat, bossFetch, fetchRecommendList, verbose } from './
|
|
5
|
+
import { requirePage, navigateToChat, bossFetch, fetchRecommendList, verbose } from './utils.js';
|
|
6
6
|
|
|
7
7
|
cli({
|
|
8
8
|
site: 'boss',
|
package/src/clis/boss/resume.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* .position-content → job being discussed + expectation
|
|
11
11
|
*/
|
|
12
12
|
import { cli, Strategy } from '../../registry.js';
|
|
13
|
-
import { requirePage, navigateToChat, findFriendByUid, clickCandidateInList } from './
|
|
13
|
+
import { requirePage, navigateToChat, findFriendByUid, clickCandidateInList } from './utils.js';
|
|
14
14
|
|
|
15
15
|
cli({
|
|
16
16
|
site: 'boss',
|
package/src/clis/boss/search.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BOSS直聘 job search — browser cookie API.
|
|
3
3
|
*/
|
|
4
4
|
import { cli, Strategy } from '../../registry.js';
|
|
5
|
-
import { requirePage, navigateTo, bossFetch, assertOk, verbose } from './
|
|
5
|
+
import { requirePage, navigateTo, bossFetch, assertOk, verbose } from './utils.js';
|
|
6
6
|
|
|
7
7
|
/** City name → BOSS Zhipin city code mapping */
|
|
8
8
|
const CITY_CODES: Record<string, string> = {
|