@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/dist/clis/cursor/new.js
CHANGED
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const newCommand =
|
|
3
|
-
site: 'cursor',
|
|
4
|
-
name: 'new',
|
|
5
|
-
description: 'Start a new Cursor chat or Composer session',
|
|
6
|
-
domain: 'localhost',
|
|
7
|
-
strategy: Strategy.UI,
|
|
8
|
-
browser: true,
|
|
9
|
-
args: [],
|
|
10
|
-
columns: ['Status'],
|
|
11
|
-
func: async (page) => {
|
|
12
|
-
// Use keyboard shortcut — most robust approach, avoids brittle DOM selectors
|
|
13
|
-
const isMac = process.platform === 'darwin';
|
|
14
|
-
await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
|
|
15
|
-
await page.wait(1);
|
|
16
|
-
return [{ Status: 'Success' }];
|
|
17
|
-
},
|
|
18
|
-
});
|
|
1
|
+
import { makeNewCommand } from '../_shared/desktop-commands.js';
|
|
2
|
+
export const newCommand = makeNewCommand('cursor', 'Cursor chat or Composer');
|
package/dist/clis/cursor/read.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { EmptyResultError } from '../../errors.js';
|
|
2
3
|
export const readCommand = cli({
|
|
3
4
|
site: 'cursor',
|
|
4
5
|
name: 'read',
|
|
@@ -36,7 +37,7 @@ export const readCommand = cli({
|
|
|
36
37
|
})()
|
|
37
38
|
`);
|
|
38
39
|
if (!history || history.length === 0) {
|
|
39
|
-
throw new
|
|
40
|
+
throw new EmptyResultError('cursor read', 'No conversation history found in Cursor.');
|
|
40
41
|
}
|
|
41
42
|
return history;
|
|
42
43
|
},
|
|
@@ -1,31 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { cli, Strategy } from '../../registry.js';
|
|
3
|
-
function makeScreenshotCommand(site) {
|
|
4
|
-
return cli({
|
|
5
|
-
site,
|
|
6
|
-
name: 'screenshot',
|
|
7
|
-
description: `Capture a snapshot of the current ${site} window (DOM + Accessibility tree)`,
|
|
8
|
-
domain: 'localhost',
|
|
9
|
-
strategy: Strategy.UI,
|
|
10
|
-
browser: true,
|
|
11
|
-
args: [
|
|
12
|
-
{ name: 'output', required: false, help: `Output file path (default: /tmp/${site}-snapshot.txt)` },
|
|
13
|
-
],
|
|
14
|
-
columns: ['Status', 'File'],
|
|
15
|
-
func: async (page, kwargs) => {
|
|
16
|
-
const outputPath = kwargs.output || `/tmp/${site}-snapshot.txt`;
|
|
17
|
-
// Get both the accessibility snapshot and the raw DOM HTML
|
|
18
|
-
const snap = await page.snapshot({ compact: true });
|
|
19
|
-
const html = await page.evaluate('document.documentElement.outerHTML');
|
|
20
|
-
const htmlPath = outputPath.replace(/\.\w+$/, '') + '-dom.html';
|
|
21
|
-
const snapPath = outputPath.replace(/\.\w+$/, '') + '-a11y.txt';
|
|
22
|
-
fs.writeFileSync(htmlPath, html);
|
|
23
|
-
fs.writeFileSync(snapPath, typeof snap === 'string' ? snap : JSON.stringify(snap, null, 2));
|
|
24
|
-
return [
|
|
25
|
-
{ Status: 'Success', File: htmlPath },
|
|
26
|
-
{ Status: 'Success', File: snapPath },
|
|
27
|
-
];
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
}
|
|
1
|
+
import { makeScreenshotCommand } from '../_shared/desktop-commands.js';
|
|
31
2
|
export const screenshotCursor = makeScreenshotCommand('cursor');
|
package/dist/clis/cursor/send.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { SelectorError } from '../../errors.js';
|
|
2
3
|
export const sendCommand = cli({
|
|
3
4
|
site: 'cursor',
|
|
4
5
|
name: 'send',
|
|
@@ -23,7 +24,7 @@ export const sendCommand = cli({
|
|
|
23
24
|
return true;
|
|
24
25
|
})(${JSON.stringify(textToInsert)})`);
|
|
25
26
|
if (!injected) {
|
|
26
|
-
throw new
|
|
27
|
+
throw new SelectorError('Cursor Composer input element');
|
|
27
28
|
}
|
|
28
29
|
// Submit the command. In Cursor, Enter usually submits the chat.
|
|
29
30
|
await page.wait(0.5);
|
|
@@ -1,21 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const statusCommand =
|
|
3
|
-
site: 'cursor',
|
|
4
|
-
name: 'status',
|
|
5
|
-
description: 'Check active CDP connection to Cursor AI Editor',
|
|
6
|
-
domain: 'localhost',
|
|
7
|
-
strategy: Strategy.UI, // Interactive UI manipulation
|
|
8
|
-
browser: true,
|
|
9
|
-
columns: ['Status', 'Url', 'Title'],
|
|
10
|
-
func: async (page) => {
|
|
11
|
-
const url = await page.evaluate('window.location.href');
|
|
12
|
-
const title = await page.evaluate('document.title');
|
|
13
|
-
return [
|
|
14
|
-
{
|
|
15
|
-
Status: 'Connected',
|
|
16
|
-
Url: url,
|
|
17
|
-
Title: title,
|
|
18
|
-
},
|
|
19
|
-
];
|
|
20
|
-
},
|
|
21
|
-
});
|
|
1
|
+
import { makeStatusCommand } from '../_shared/desktop-commands.js';
|
|
2
|
+
export const statusCommand = makeStatusCommand('cursor', 'Cursor AI Editor');
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
site: dictionary
|
|
2
|
+
name: examples
|
|
3
|
+
description: Read real-world example sentences utilizing the word
|
|
4
|
+
domain: api.dictionaryapi.dev
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
word:
|
|
10
|
+
type: string
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: Word to get example sentences for
|
|
14
|
+
|
|
15
|
+
pipeline:
|
|
16
|
+
- fetch:
|
|
17
|
+
url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
|
|
18
|
+
|
|
19
|
+
- map:
|
|
20
|
+
word: "${{ item.word }}"
|
|
21
|
+
example: "${{ (() => { if (item.meanings) { for (const m of item.meanings) { if (m.definitions) { for (const d of m.definitions) { if (d.example) return d.example; } } } } return 'No example found in API.'; })() }}"
|
|
22
|
+
|
|
23
|
+
- limit: 1
|
|
24
|
+
|
|
25
|
+
columns: [word, example]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
site: dictionary
|
|
2
|
+
name: search
|
|
3
|
+
description: Search the Free Dictionary API for definitions, parts of speech, and pronunciations.
|
|
4
|
+
domain: api.dictionaryapi.dev
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
word:
|
|
10
|
+
type: string
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: Word to define (e.g., serendipity)
|
|
14
|
+
|
|
15
|
+
pipeline:
|
|
16
|
+
- fetch:
|
|
17
|
+
url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
|
|
18
|
+
|
|
19
|
+
- map:
|
|
20
|
+
word: "${{ item.word }}"
|
|
21
|
+
phonetic: "${{ (() => { if (item.phonetic) return item.phonetic; if (item.phonetics) { for (const p of item.phonetics) { if (p.text) return p.text; } } return ''; })() }}"
|
|
22
|
+
type: "${{ (() => { if (item.meanings && item.meanings[0] && item.meanings[0].partOfSpeech) return item.meanings[0].partOfSpeech; return 'N/A'; })() }}"
|
|
23
|
+
definition: "${{ (() => { if (item.meanings && item.meanings[0] && item.meanings[0].definitions && item.meanings[0].definitions[0] && item.meanings[0].definitions[0].definition) return item.meanings[0].definitions[0].definition; return 'No definition found in API.'; })() }}"
|
|
24
|
+
|
|
25
|
+
- limit: 1
|
|
26
|
+
|
|
27
|
+
columns: [word, phonetic, type, definition]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
site: dictionary
|
|
2
|
+
name: synonyms
|
|
3
|
+
description: Find synonyms for a specific word
|
|
4
|
+
domain: api.dictionaryapi.dev
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
word:
|
|
10
|
+
type: string
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: Word to find synonyms for (e.g., serendipity)
|
|
14
|
+
|
|
15
|
+
pipeline:
|
|
16
|
+
- fetch:
|
|
17
|
+
url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
|
|
18
|
+
|
|
19
|
+
- map:
|
|
20
|
+
word: "${{ item.word }}"
|
|
21
|
+
synonyms: "${{ (() => { const s = new Set(); if (item.meanings) { for (const m of item.meanings) { if (m.synonyms) { for (const syn of m.synonyms) s.add(syn); } if (m.definitions) { for (const d of m.definitions) { if (d.synonyms) { for (const syn of d.synonyms) s.add(syn); } } } } } const arr = Array.from(s); return arr.length > 0 ? arr.slice(0, 5).join(', ') : 'No synonyms found in API.'; })() }}"
|
|
22
|
+
|
|
23
|
+
- limit: 1
|
|
24
|
+
|
|
25
|
+
columns: [word, synonyms]
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Douban
|
|
2
|
+
* Douban adapter utilities.
|
|
3
3
|
*/
|
|
4
4
|
import type { IPage } from '../../types.js';
|
|
5
|
+
export declare function loadDoubanBookHot(page: IPage, limit: number): Promise<any[]>;
|
|
6
|
+
export declare function loadDoubanMovieHot(page: IPage, limit: number): Promise<any[]>;
|
|
7
|
+
export declare function searchDouban(page: IPage, type: string, keyword: string, limit: number): Promise<any[]>;
|
|
5
8
|
/**
|
|
6
9
|
* Get current user's Douban ID from movie.douban.com/mine page
|
|
7
10
|
*/
|
|
@@ -1,6 +1,161 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Douban
|
|
2
|
+
* Douban adapter utilities.
|
|
3
3
|
*/
|
|
4
|
+
import { CliError } from '../../errors.js';
|
|
5
|
+
function clampLimit(limit) {
|
|
6
|
+
return Math.max(1, Math.min(limit || 20, 50));
|
|
7
|
+
}
|
|
8
|
+
async function ensureDoubanReady(page) {
|
|
9
|
+
const state = await page.evaluate(`
|
|
10
|
+
(() => {
|
|
11
|
+
const title = (document.title || '').trim();
|
|
12
|
+
const href = (location.href || '').trim();
|
|
13
|
+
const blocked = href.includes('sec.douban.com') || /登录跳转/.test(title) || /异常请求/.test(document.body?.innerText || '');
|
|
14
|
+
return { blocked, title, href };
|
|
15
|
+
})()
|
|
16
|
+
`);
|
|
17
|
+
if (state?.blocked) {
|
|
18
|
+
throw new CliError('AUTH_REQUIRED', 'Douban requires a logged-in browser session before these commands can load data.', 'Please sign in to douban.com in the browser that opencli reuses, then rerun the command.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function loadDoubanBookHot(page, limit) {
|
|
22
|
+
const safeLimit = clampLimit(limit);
|
|
23
|
+
await page.goto('https://book.douban.com/chart');
|
|
24
|
+
await page.wait(4);
|
|
25
|
+
await ensureDoubanReady(page);
|
|
26
|
+
const data = await page.evaluate(`
|
|
27
|
+
(() => {
|
|
28
|
+
const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
|
|
29
|
+
const books = [];
|
|
30
|
+
for (const el of Array.from(document.querySelectorAll('.media.clearfix'))) {
|
|
31
|
+
try {
|
|
32
|
+
const titleEl = el.querySelector('h2 a[href*="/subject/"]');
|
|
33
|
+
const title = normalize(titleEl?.textContent);
|
|
34
|
+
let url = titleEl?.getAttribute('href') || '';
|
|
35
|
+
if (!title || !url) continue;
|
|
36
|
+
if (!url.startsWith('http')) url = 'https://book.douban.com' + url;
|
|
37
|
+
|
|
38
|
+
const info = normalize(el.querySelector('.subject-abstract, .pl, .pub')?.textContent);
|
|
39
|
+
const infoParts = info.split('/').map((part) => part.trim()).filter(Boolean);
|
|
40
|
+
const ratingText = normalize(el.querySelector('.subject-rating .font-small, .rating_nums, .rating')?.textContent);
|
|
41
|
+
const quote = Array.from(el.querySelectorAll('.subject-tags .tag'))
|
|
42
|
+
.map((node) => normalize(node.textContent))
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.join(' / ');
|
|
45
|
+
|
|
46
|
+
books.push({
|
|
47
|
+
rank: parseInt(normalize(el.querySelector('.green-num-box')?.textContent), 10) || books.length + 1,
|
|
48
|
+
title,
|
|
49
|
+
rating: parseFloat(ratingText) || 0,
|
|
50
|
+
quote,
|
|
51
|
+
author: infoParts[0] || '',
|
|
52
|
+
publisher: infoParts.find((part) => /出版社|出版公司|Press/i.test(part)) || infoParts[2] || '',
|
|
53
|
+
year: infoParts.find((part) => /\\d{4}(?:-\\d{1,2})?/.test(part))?.match(/\\d{4}/)?.[0] || '',
|
|
54
|
+
price: infoParts.find((part) => /元|USD|\\$|¥/.test(part)) || '',
|
|
55
|
+
url,
|
|
56
|
+
cover: el.querySelector('img')?.getAttribute('src') || '',
|
|
57
|
+
});
|
|
58
|
+
} catch {}
|
|
59
|
+
}
|
|
60
|
+
return books.slice(0, ${safeLimit});
|
|
61
|
+
})()
|
|
62
|
+
`);
|
|
63
|
+
return Array.isArray(data) ? data : [];
|
|
64
|
+
}
|
|
65
|
+
export async function loadDoubanMovieHot(page, limit) {
|
|
66
|
+
const safeLimit = clampLimit(limit);
|
|
67
|
+
await page.goto('https://movie.douban.com/chart');
|
|
68
|
+
await page.wait(4);
|
|
69
|
+
await ensureDoubanReady(page);
|
|
70
|
+
const data = await page.evaluate(`
|
|
71
|
+
(() => {
|
|
72
|
+
const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
|
|
73
|
+
const results = [];
|
|
74
|
+
for (const el of Array.from(document.querySelectorAll('.item'))) {
|
|
75
|
+
const titleEl = el.querySelector('.pl2 a');
|
|
76
|
+
const title = normalize(titleEl?.textContent);
|
|
77
|
+
let url = titleEl?.getAttribute('href') || '';
|
|
78
|
+
if (!title || !url) continue;
|
|
79
|
+
if (!url.startsWith('http')) url = 'https://movie.douban.com' + url;
|
|
80
|
+
|
|
81
|
+
const info = normalize(el.querySelector('.pl2 p')?.textContent);
|
|
82
|
+
const infoParts = info.split('/').map((part) => part.trim()).filter(Boolean);
|
|
83
|
+
const releaseIndex = (() => {
|
|
84
|
+
for (let i = infoParts.length - 1; i >= 0; i -= 1) {
|
|
85
|
+
if (/\\d{4}-\\d{2}-\\d{2}|\\d{4}\\/\\d{2}\\/\\d{2}/.test(infoParts[i])) return i;
|
|
86
|
+
}
|
|
87
|
+
return -1;
|
|
88
|
+
})();
|
|
89
|
+
const directorPart = releaseIndex >= 1 ? infoParts[releaseIndex - 1] : '';
|
|
90
|
+
const regionPart = releaseIndex >= 2 ? infoParts[releaseIndex - 2] : '';
|
|
91
|
+
const yearMatch = info.match(/\\b(19|20)\\d{2}\\b/);
|
|
92
|
+
results.push({
|
|
93
|
+
rank: results.length + 1,
|
|
94
|
+
title,
|
|
95
|
+
rating: parseFloat(normalize(el.querySelector('.rating_nums')?.textContent)) || 0,
|
|
96
|
+
quote: normalize(el.querySelector('.inq')?.textContent),
|
|
97
|
+
director: directorPart.replace(/^导演:\\s*/, ''),
|
|
98
|
+
year: yearMatch?.[0] || '',
|
|
99
|
+
region: regionPart,
|
|
100
|
+
url,
|
|
101
|
+
cover: el.querySelector('img')?.getAttribute('src') || '',
|
|
102
|
+
});
|
|
103
|
+
if (results.length >= ${safeLimit}) break;
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
})()
|
|
107
|
+
`);
|
|
108
|
+
return Array.isArray(data) ? data : [];
|
|
109
|
+
}
|
|
110
|
+
export async function searchDouban(page, type, keyword, limit) {
|
|
111
|
+
const safeLimit = clampLimit(limit);
|
|
112
|
+
await page.goto(`https://search.douban.com/${encodeURIComponent(type)}/subject_search?search_text=${encodeURIComponent(keyword)}`);
|
|
113
|
+
await page.wait(2);
|
|
114
|
+
await ensureDoubanReady(page);
|
|
115
|
+
const data = await page.evaluate(`
|
|
116
|
+
(async () => {
|
|
117
|
+
const type = ${JSON.stringify(type)};
|
|
118
|
+
const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
|
|
119
|
+
const seen = new Set();
|
|
120
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < 20; i += 1) {
|
|
123
|
+
if (document.querySelector('.item-root .title-text, .item-root .title a')) break;
|
|
124
|
+
await sleep(300);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const items = Array.from(document.querySelectorAll('.item-root'));
|
|
128
|
+
|
|
129
|
+
const results = [];
|
|
130
|
+
for (const el of items) {
|
|
131
|
+
const titleEl = el.querySelector('.title-text, .title a, a[title]');
|
|
132
|
+
const title = normalize(titleEl?.textContent) || normalize(titleEl?.getAttribute('title'));
|
|
133
|
+
let url = titleEl?.getAttribute('href') || '';
|
|
134
|
+
if (!title || !url) continue;
|
|
135
|
+
if (!url.startsWith('http')) url = 'https://search.douban.com' + url;
|
|
136
|
+
if (!url.includes('/subject/') || seen.has(url)) continue;
|
|
137
|
+
seen.add(url);
|
|
138
|
+
const ratingText = normalize(el.querySelector('.rating_nums')?.textContent);
|
|
139
|
+
const abstract = normalize(
|
|
140
|
+
el.querySelector('.meta.abstract, .meta, .abstract, p')?.textContent,
|
|
141
|
+
);
|
|
142
|
+
results.push({
|
|
143
|
+
rank: results.length + 1,
|
|
144
|
+
id: url.match(/subject\\/(\\d+)/)?.[1] || '',
|
|
145
|
+
type,
|
|
146
|
+
title,
|
|
147
|
+
rating: ratingText.includes('.') ? parseFloat(ratingText) : 0,
|
|
148
|
+
abstract: abstract.slice(0, 100) + (abstract.length > 100 ? '...' : ''),
|
|
149
|
+
url,
|
|
150
|
+
cover: el.querySelector('img')?.getAttribute('src') || '',
|
|
151
|
+
});
|
|
152
|
+
if (results.length >= ${safeLimit}) break;
|
|
153
|
+
}
|
|
154
|
+
return results;
|
|
155
|
+
})()
|
|
156
|
+
`);
|
|
157
|
+
return Array.isArray(data) ? data : [];
|
|
158
|
+
}
|
|
4
159
|
/**
|
|
5
160
|
* Get current user's Douban ID from movie.douban.com/mine page
|
|
6
161
|
*/
|
package/dist/clis/doubao/ask.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
-
import { DOUBAO_DOMAIN, getDoubaoTranscriptLines, getDoubaoVisibleTurns, sendDoubaoMessage, waitForDoubaoResponse } from './
|
|
2
|
+
import { DOUBAO_DOMAIN, getDoubaoTranscriptLines, getDoubaoVisibleTurns, sendDoubaoMessage, waitForDoubaoResponse } from './utils.js';
|
|
3
3
|
export const askCommand = cli({
|
|
4
4
|
site: 'doubao',
|
|
5
5
|
name: 'ask',
|
package/dist/clis/doubao/new.js
CHANGED
package/dist/clis/doubao/read.js
CHANGED
package/dist/clis/doubao/send.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
-
import { SEL, injectTextScript, clickSendScript, pollResponseScript } from './
|
|
2
|
+
import { SEL, injectTextScript, clickSendScript, pollResponseScript } from './utils.js';
|
|
3
3
|
export const askCommand = cli({
|
|
4
4
|
site: 'doubao-app',
|
|
5
5
|
name: 'ask',
|
package/dist/clis/grok/ask.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IPage } from '../../types.js';
|
|
1
2
|
declare function normalizeBubbleText(value: unknown): string;
|
|
2
3
|
declare function normalizeBooleanFlag(value: unknown): boolean;
|
|
3
4
|
declare function pickLatestAssistantCandidate(bubbles: unknown[], baselineCount: number, prompt: string): string;
|
|
@@ -5,11 +6,14 @@ declare function updateStableState(previousText: string, stableCount: number, ne
|
|
|
5
6
|
previousText: string;
|
|
6
7
|
stableCount: number;
|
|
7
8
|
};
|
|
9
|
+
/** Check whether the tab is already on grok.com (any path). */
|
|
10
|
+
declare function isOnGrok(page: IPage): Promise<boolean>;
|
|
8
11
|
export declare const askCommand: import("../../registry.js").CliCommand;
|
|
9
12
|
export declare const __test__: {
|
|
10
13
|
pickLatestAssistantCandidate: typeof pickLatestAssistantCandidate;
|
|
11
14
|
updateStableState: typeof updateStableState;
|
|
12
15
|
normalizeBooleanFlag: typeof normalizeBooleanFlag;
|
|
13
16
|
normalizeBubbleText: typeof normalizeBubbleText;
|
|
17
|
+
isOnGrok: typeof isOnGrok;
|
|
14
18
|
};
|
|
15
19
|
export {};
|
package/dist/clis/grok/ask.js
CHANGED
|
@@ -35,21 +35,33 @@ function updateStableState(previousText, stableCount, nextText) {
|
|
|
35
35
|
return { previousText, stableCount: stableCount + 1 };
|
|
36
36
|
return { previousText: nextText, stableCount: 0 };
|
|
37
37
|
}
|
|
38
|
+
/** Check whether the tab is already on grok.com (any path). */
|
|
39
|
+
async function isOnGrok(page) {
|
|
40
|
+
// catch handles blank tabs (about:blank) or detached pages
|
|
41
|
+
const url = await page.evaluate('window.location.href').catch(() => '');
|
|
42
|
+
if (typeof url !== 'string' || !url)
|
|
43
|
+
return false;
|
|
44
|
+
try {
|
|
45
|
+
const hostname = new URL(url).hostname;
|
|
46
|
+
return hostname === 'grok.com' || hostname.endsWith('.grok.com');
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
38
52
|
async function runDefaultAsk(page, prompt, timeoutMs, newChat) {
|
|
39
53
|
if (newChat) {
|
|
54
|
+
// Explicitly start a fresh conversation via the homepage
|
|
40
55
|
await page.goto(GROK_URL);
|
|
41
56
|
await page.wait(2);
|
|
42
|
-
await page
|
|
43
|
-
const btn = [...document.querySelectorAll('a, button')].find(b => {
|
|
44
|
-
const t = (b.textContent || '').trim().toLowerCase();
|
|
45
|
-
return t.includes('new') || b.getAttribute('href') === '/';
|
|
46
|
-
});
|
|
47
|
-
if (btn) btn.click();
|
|
48
|
-
})()`);
|
|
57
|
+
await tryStartFreshChat(page);
|
|
49
58
|
await page.wait(2);
|
|
50
59
|
}
|
|
51
|
-
await page
|
|
52
|
-
|
|
60
|
+
else if (!(await isOnGrok(page))) {
|
|
61
|
+
// First invocation or tab was recycled — navigate to Grok
|
|
62
|
+
await page.goto(GROK_URL);
|
|
63
|
+
await page.wait(3);
|
|
64
|
+
}
|
|
53
65
|
const promptJson = JSON.stringify(prompt);
|
|
54
66
|
const sendResult = await page.evaluate(`(async () => {
|
|
55
67
|
try {
|
|
@@ -212,11 +224,16 @@ async function sendPromptViaExplicitWeb(page, prompt) {
|
|
|
212
224
|
})()`);
|
|
213
225
|
}
|
|
214
226
|
async function runExplicitWebAsk(page, prompt, timeoutMs, newChat) {
|
|
215
|
-
await page.goto(GROK_URL, { settleMs: 2000 });
|
|
216
227
|
if (newChat) {
|
|
228
|
+
// Navigate to homepage and start a fresh conversation
|
|
229
|
+
await page.goto(GROK_URL, { settleMs: 2000 });
|
|
217
230
|
await tryStartFreshChat(page);
|
|
218
231
|
await page.wait(2);
|
|
219
232
|
}
|
|
233
|
+
else if (!(await isOnGrok(page))) {
|
|
234
|
+
// First invocation or tab was recycled — navigate to Grok
|
|
235
|
+
await page.goto(GROK_URL, { settleMs: 2000 });
|
|
236
|
+
}
|
|
220
237
|
const baselineBubbles = await getBubbleTexts(page);
|
|
221
238
|
const sendResult = await sendPromptViaExplicitWeb(page, prompt);
|
|
222
239
|
if (!sendResult?.ok) {
|
|
@@ -271,4 +288,5 @@ export const __test__ = {
|
|
|
271
288
|
updateStableState,
|
|
272
289
|
normalizeBooleanFlag,
|
|
273
290
|
normalizeBubbleText,
|
|
291
|
+
isOnGrok,
|
|
274
292
|
};
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { __test__ } from './ask.js';
|
|
3
3
|
describe('grok ask helpers', () => {
|
|
4
|
+
describe('isOnGrok', () => {
|
|
5
|
+
const fakePage = (url) => ({ evaluate: () => url instanceof Error ? Promise.reject(url) : Promise.resolve(url) });
|
|
6
|
+
it('returns true for grok.com URLs', async () => {
|
|
7
|
+
expect(await __test__.isOnGrok(fakePage('https://grok.com/'))).toBe(true);
|
|
8
|
+
expect(await __test__.isOnGrok(fakePage('https://grok.com/chat/abc123'))).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
it('returns true for grok.com subdomains', async () => {
|
|
11
|
+
expect(await __test__.isOnGrok(fakePage('https://api.grok.com/v1'))).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
it('returns false for non-grok domains', async () => {
|
|
14
|
+
expect(await __test__.isOnGrok(fakePage('https://fakegrok.com/'))).toBe(false);
|
|
15
|
+
expect(await __test__.isOnGrok(fakePage('https://example.com/?next=grok.com'))).toBe(false);
|
|
16
|
+
expect(await __test__.isOnGrok(fakePage('about:blank'))).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('returns false when evaluate throws (detached tab)', async () => {
|
|
19
|
+
expect(await __test__.isOnGrok(fakePage(new Error('detached')))).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
4
22
|
it('normalizes boolean flags for explicit web routing', () => {
|
|
5
23
|
expect(__test__.normalizeBooleanFlag(true)).toBe(true);
|
|
6
24
|
expect(__test__.normalizeBooleanFlag('true')).toBe(true);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|