@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/plugin.test.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for plugin management: install, uninstall, list.
|
|
2
|
+
* Tests for plugin management: install, uninstall, list, and lock file support.
|
|
3
3
|
*/
|
|
4
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
5
5
|
import * as fs from 'node:fs';
|
|
6
|
+
import * as os from 'node:os';
|
|
6
7
|
import * as path from 'node:path';
|
|
7
8
|
import { PLUGINS_DIR } from './discovery.js';
|
|
8
|
-
import
|
|
9
|
+
import * as pluginModule from './plugin.js';
|
|
10
|
+
const { LOCK_FILE, _getCommitHash, listPlugins, _readLockFile, _resolveEsbuildBin, uninstallPlugin, updatePlugin, _parseSource, _updateAllPlugins, _validatePluginStructure, _writeLockFile, } = pluginModule;
|
|
9
11
|
describe('parseSource', () => {
|
|
10
12
|
it('parses github:user/repo format', () => {
|
|
11
13
|
const result = _parseSource('github:ByteYue/opencli-plugin-github-trending');
|
|
@@ -34,6 +36,131 @@ describe('parseSource', () => {
|
|
|
34
36
|
expect(_parseSource('npm:some-package')).toBeNull();
|
|
35
37
|
});
|
|
36
38
|
});
|
|
39
|
+
describe('validatePluginStructure', () => {
|
|
40
|
+
const testDir = path.join(PLUGINS_DIR, '__test-validate__');
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
43
|
+
});
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
try {
|
|
46
|
+
fs.rmSync(testDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
catch { }
|
|
49
|
+
});
|
|
50
|
+
it('returns invalid for non-existent directory', () => {
|
|
51
|
+
const res = _validatePluginStructure(path.join(PLUGINS_DIR, '__does_not_exist__'));
|
|
52
|
+
expect(res.valid).toBe(false);
|
|
53
|
+
expect(res.errors[0]).toContain('does not exist');
|
|
54
|
+
});
|
|
55
|
+
it('returns invalid for empty directory', () => {
|
|
56
|
+
const res = _validatePluginStructure(testDir);
|
|
57
|
+
expect(res.valid).toBe(false);
|
|
58
|
+
expect(res.errors[0]).toContain('No command files found');
|
|
59
|
+
});
|
|
60
|
+
it('returns valid for YAML plugin', () => {
|
|
61
|
+
fs.writeFileSync(path.join(testDir, 'cmd.yaml'), 'site: test');
|
|
62
|
+
const res = _validatePluginStructure(testDir);
|
|
63
|
+
expect(res.valid).toBe(true);
|
|
64
|
+
expect(res.errors).toHaveLength(0);
|
|
65
|
+
});
|
|
66
|
+
it('returns valid for JS plugin', () => {
|
|
67
|
+
fs.writeFileSync(path.join(testDir, 'cmd.js'), 'console.log("hi");');
|
|
68
|
+
const res = _validatePluginStructure(testDir);
|
|
69
|
+
expect(res.valid).toBe(true);
|
|
70
|
+
expect(res.errors).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
it('returns invalid for TS plugin without package.json', () => {
|
|
73
|
+
fs.writeFileSync(path.join(testDir, 'cmd.ts'), 'console.log("hi");');
|
|
74
|
+
const res = _validatePluginStructure(testDir);
|
|
75
|
+
expect(res.valid).toBe(false);
|
|
76
|
+
expect(res.errors[0]).toContain('contains .ts files but no package.json');
|
|
77
|
+
});
|
|
78
|
+
it('returns invalid for TS plugin with missing type: module', () => {
|
|
79
|
+
fs.writeFileSync(path.join(testDir, 'cmd.ts'), 'console.log("hi");');
|
|
80
|
+
fs.writeFileSync(path.join(testDir, 'package.json'), JSON.stringify({ name: 'test' }));
|
|
81
|
+
const res = _validatePluginStructure(testDir);
|
|
82
|
+
expect(res.valid).toBe(false);
|
|
83
|
+
expect(res.errors[0]).toContain('must have "type": "module"');
|
|
84
|
+
});
|
|
85
|
+
it('returns valid for TS plugin with correct package.json', () => {
|
|
86
|
+
fs.writeFileSync(path.join(testDir, 'cmd.ts'), 'console.log("hi");');
|
|
87
|
+
fs.writeFileSync(path.join(testDir, 'package.json'), JSON.stringify({ type: 'module' }));
|
|
88
|
+
const res = _validatePluginStructure(testDir);
|
|
89
|
+
expect(res.valid).toBe(true);
|
|
90
|
+
expect(res.errors).toHaveLength(0);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('lock file', () => {
|
|
94
|
+
const backupPath = `${LOCK_FILE}.test-backup`;
|
|
95
|
+
let hadOriginal = false;
|
|
96
|
+
beforeEach(() => {
|
|
97
|
+
hadOriginal = fs.existsSync(LOCK_FILE);
|
|
98
|
+
if (hadOriginal) {
|
|
99
|
+
fs.mkdirSync(path.dirname(backupPath), { recursive: true });
|
|
100
|
+
fs.copyFileSync(LOCK_FILE, backupPath);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
afterEach(() => {
|
|
104
|
+
if (hadOriginal) {
|
|
105
|
+
fs.copyFileSync(backupPath, LOCK_FILE);
|
|
106
|
+
fs.unlinkSync(backupPath);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
fs.unlinkSync(LOCK_FILE);
|
|
111
|
+
}
|
|
112
|
+
catch { }
|
|
113
|
+
});
|
|
114
|
+
it('reads empty lock when file does not exist', () => {
|
|
115
|
+
try {
|
|
116
|
+
fs.unlinkSync(LOCK_FILE);
|
|
117
|
+
}
|
|
118
|
+
catch { }
|
|
119
|
+
expect(_readLockFile()).toEqual({});
|
|
120
|
+
});
|
|
121
|
+
it('round-trips lock entries', () => {
|
|
122
|
+
const entries = {
|
|
123
|
+
'test-plugin': {
|
|
124
|
+
source: 'https://github.com/user/repo.git',
|
|
125
|
+
commitHash: 'abc1234567890def',
|
|
126
|
+
installedAt: '2025-01-01T00:00:00.000Z',
|
|
127
|
+
},
|
|
128
|
+
'another-plugin': {
|
|
129
|
+
source: 'https://github.com/user/another.git',
|
|
130
|
+
commitHash: 'def4567890123abc',
|
|
131
|
+
installedAt: '2025-02-01T00:00:00.000Z',
|
|
132
|
+
updatedAt: '2025-03-01T00:00:00.000Z',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
_writeLockFile(entries);
|
|
136
|
+
expect(_readLockFile()).toEqual(entries);
|
|
137
|
+
});
|
|
138
|
+
it('handles malformed lock file gracefully', () => {
|
|
139
|
+
fs.mkdirSync(path.dirname(LOCK_FILE), { recursive: true });
|
|
140
|
+
fs.writeFileSync(LOCK_FILE, 'not valid json');
|
|
141
|
+
expect(_readLockFile()).toEqual({});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe('getCommitHash', () => {
|
|
145
|
+
it('returns a hash for a git repo', () => {
|
|
146
|
+
const hash = _getCommitHash(process.cwd());
|
|
147
|
+
expect(hash).toBeDefined();
|
|
148
|
+
expect(hash).toMatch(/^[0-9a-f]{40}$/);
|
|
149
|
+
});
|
|
150
|
+
it('returns undefined for non-git directory', () => {
|
|
151
|
+
expect(_getCommitHash(os.tmpdir())).toBeUndefined();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe('resolveEsbuildBin', () => {
|
|
155
|
+
it('resolves a usable esbuild executable path', () => {
|
|
156
|
+
const binPath = _resolveEsbuildBin();
|
|
157
|
+
expect(binPath).not.toBeNull();
|
|
158
|
+
expect(typeof binPath).toBe('string');
|
|
159
|
+
expect(fs.existsSync(binPath)).toBe(true);
|
|
160
|
+
// On Windows the resolved path ends with 'esbuild.cmd', on Unix 'esbuild'
|
|
161
|
+
expect(binPath).toMatch(/esbuild(\.cmd)?$/);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
37
164
|
describe('listPlugins', () => {
|
|
38
165
|
const testDir = path.join(PLUGINS_DIR, '__test-list-plugin__');
|
|
39
166
|
afterEach(() => {
|
|
@@ -50,6 +177,24 @@ describe('listPlugins', () => {
|
|
|
50
177
|
expect(found).toBeDefined();
|
|
51
178
|
expect(found.commands).toContain('hello');
|
|
52
179
|
});
|
|
180
|
+
it('includes version metadata from the lock file', () => {
|
|
181
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
182
|
+
fs.writeFileSync(path.join(testDir, 'hello.yaml'), 'site: test\nname: hello\n');
|
|
183
|
+
const lock = _readLockFile();
|
|
184
|
+
lock['__test-list-plugin__'] = {
|
|
185
|
+
source: 'https://github.com/user/repo.git',
|
|
186
|
+
commitHash: 'abcdef1234567890abcdef1234567890abcdef12',
|
|
187
|
+
installedAt: '2025-01-01T00:00:00.000Z',
|
|
188
|
+
};
|
|
189
|
+
_writeLockFile(lock);
|
|
190
|
+
const plugins = listPlugins();
|
|
191
|
+
const found = plugins.find(p => p.name === '__test-list-plugin__');
|
|
192
|
+
expect(found).toBeDefined();
|
|
193
|
+
expect(found.version).toBe('abcdef1');
|
|
194
|
+
expect(found.installedAt).toBe('2025-01-01T00:00:00.000Z');
|
|
195
|
+
delete lock['__test-list-plugin__'];
|
|
196
|
+
_writeLockFile(lock);
|
|
197
|
+
});
|
|
53
198
|
it('returns empty array when no plugins dir', () => {
|
|
54
199
|
// listPlugins should handle missing dir gracefully
|
|
55
200
|
const plugins = listPlugins();
|
|
@@ -70,6 +215,19 @@ describe('uninstallPlugin', () => {
|
|
|
70
215
|
uninstallPlugin('__test-uninstall__');
|
|
71
216
|
expect(fs.existsSync(testDir)).toBe(false);
|
|
72
217
|
});
|
|
218
|
+
it('removes lock entry on uninstall', () => {
|
|
219
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
220
|
+
fs.writeFileSync(path.join(testDir, 'test.yaml'), 'site: test');
|
|
221
|
+
const lock = _readLockFile();
|
|
222
|
+
lock['__test-uninstall__'] = {
|
|
223
|
+
source: 'https://github.com/user/repo.git',
|
|
224
|
+
commitHash: 'abc123',
|
|
225
|
+
installedAt: '2025-01-01T00:00:00.000Z',
|
|
226
|
+
};
|
|
227
|
+
_writeLockFile(lock);
|
|
228
|
+
uninstallPlugin('__test-uninstall__');
|
|
229
|
+
expect(_readLockFile()['__test-uninstall__']).toBeUndefined();
|
|
230
|
+
});
|
|
73
231
|
it('throws for non-existent plugin', () => {
|
|
74
232
|
expect(() => uninstallPlugin('__nonexistent__')).toThrow('not installed');
|
|
75
233
|
});
|
|
@@ -79,3 +237,61 @@ describe('updatePlugin', () => {
|
|
|
79
237
|
expect(() => updatePlugin('__nonexistent__')).toThrow('not installed');
|
|
80
238
|
});
|
|
81
239
|
});
|
|
240
|
+
vi.mock('node:child_process', () => {
|
|
241
|
+
return {
|
|
242
|
+
execFileSync: vi.fn((_cmd, args, opts) => {
|
|
243
|
+
if (Array.isArray(args) && args[0] === 'rev-parse' && args[1] === 'HEAD') {
|
|
244
|
+
if (opts?.cwd === os.tmpdir()) {
|
|
245
|
+
throw new Error('not a git repository');
|
|
246
|
+
}
|
|
247
|
+
return '1234567890abcdef1234567890abcdef12345678\n';
|
|
248
|
+
}
|
|
249
|
+
if (opts && opts.cwd && String(opts.cwd).endsWith('plugin-b')) {
|
|
250
|
+
throw new Error('Network error');
|
|
251
|
+
}
|
|
252
|
+
return '';
|
|
253
|
+
}),
|
|
254
|
+
execSync: vi.fn(() => ''),
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
describe('updateAllPlugins', () => {
|
|
258
|
+
const testDirA = path.join(PLUGINS_DIR, 'plugin-a');
|
|
259
|
+
const testDirB = path.join(PLUGINS_DIR, 'plugin-b');
|
|
260
|
+
const testDirC = path.join(PLUGINS_DIR, 'plugin-c');
|
|
261
|
+
beforeEach(() => {
|
|
262
|
+
fs.mkdirSync(testDirA, { recursive: true });
|
|
263
|
+
fs.mkdirSync(testDirB, { recursive: true });
|
|
264
|
+
fs.mkdirSync(testDirC, { recursive: true });
|
|
265
|
+
fs.writeFileSync(path.join(testDirA, 'cmd.yaml'), 'site: a');
|
|
266
|
+
fs.writeFileSync(path.join(testDirB, 'cmd.yaml'), 'site: b');
|
|
267
|
+
fs.writeFileSync(path.join(testDirC, 'cmd.yaml'), 'site: c');
|
|
268
|
+
});
|
|
269
|
+
afterEach(() => {
|
|
270
|
+
try {
|
|
271
|
+
fs.rmSync(testDirA, { recursive: true });
|
|
272
|
+
}
|
|
273
|
+
catch { }
|
|
274
|
+
try {
|
|
275
|
+
fs.rmSync(testDirB, { recursive: true });
|
|
276
|
+
}
|
|
277
|
+
catch { }
|
|
278
|
+
try {
|
|
279
|
+
fs.rmSync(testDirC, { recursive: true });
|
|
280
|
+
}
|
|
281
|
+
catch { }
|
|
282
|
+
vi.clearAllMocks();
|
|
283
|
+
});
|
|
284
|
+
it('collects successes and failures without throwing', () => {
|
|
285
|
+
const results = _updateAllPlugins();
|
|
286
|
+
const resA = results.find(r => r.name === 'plugin-a');
|
|
287
|
+
const resB = results.find(r => r.name === 'plugin-b');
|
|
288
|
+
const resC = results.find(r => r.name === 'plugin-c');
|
|
289
|
+
expect(resA).toBeDefined();
|
|
290
|
+
expect(resA.success).toBe(true);
|
|
291
|
+
expect(resB).toBeDefined();
|
|
292
|
+
expect(resB.success).toBe(false);
|
|
293
|
+
expect(resB.error).toContain('Network error');
|
|
294
|
+
expect(resC).toBeDefined();
|
|
295
|
+
expect(resC.success).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
});
|
package/dist/record.js
CHANGED
|
@@ -17,7 +17,8 @@ import * as readline from 'node:readline';
|
|
|
17
17
|
import chalk from 'chalk';
|
|
18
18
|
import yaml from 'js-yaml';
|
|
19
19
|
import { sendCommand } from './browser/daemon-client.js';
|
|
20
|
-
import {
|
|
20
|
+
import { SEARCH_PARAMS, PAGINATION_PARAMS, FIELD_ROLES } from './constants.js';
|
|
21
|
+
import { urlToPattern, findArrayPath, inferCapabilityName, inferStrategy, detectAuthFromContent, classifyQueryParams, } from './analysis.js';
|
|
21
22
|
// ── Interceptor JS ─────────────────────────────────────────────────────────
|
|
22
23
|
/**
|
|
23
24
|
* Generates a full-capture interceptor that stores {url, method, status, body}
|
|
@@ -110,92 +111,6 @@ function generateReadRecordedJs() {
|
|
|
110
111
|
`;
|
|
111
112
|
}
|
|
112
113
|
// ── Analysis helpers ───────────────────────────────────────────────────────
|
|
113
|
-
function urlToPattern(url) {
|
|
114
|
-
try {
|
|
115
|
-
const p = new URL(url);
|
|
116
|
-
const pathNorm = p.pathname
|
|
117
|
-
.replace(/\/\d+/g, '/{id}')
|
|
118
|
-
.replace(/\/[0-9a-fA-F]{8,}/g, '/{hex}')
|
|
119
|
-
.replace(/\/BV[a-zA-Z0-9]{10}/g, '/{bvid}');
|
|
120
|
-
const params = [];
|
|
121
|
-
p.searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k))
|
|
122
|
-
params.push(k); });
|
|
123
|
-
return `${p.host}${pathNorm}${params.length ? '?' + params.sort().map(k => `${k}={}`).join('&') : ''}`;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return url;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function detectAuthIndicators(url, body) {
|
|
130
|
-
const indicators = [];
|
|
131
|
-
// Heuristic: if body contains sign/w_rid fields, it's likely signed
|
|
132
|
-
if (body && typeof body === 'object') {
|
|
133
|
-
const keys = Object.keys(body).map(k => k.toLowerCase());
|
|
134
|
-
if (keys.some(k => k.includes('sign') || k === 'w_rid' || k.includes('token'))) {
|
|
135
|
-
indicators.push('signature');
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// Check URL for common auth patterns
|
|
139
|
-
if (url.includes('/wbi/') || url.includes('w_rid='))
|
|
140
|
-
indicators.push('signature');
|
|
141
|
-
if (url.includes('bearer') || url.includes('access_token'))
|
|
142
|
-
indicators.push('bearer');
|
|
143
|
-
return indicators;
|
|
144
|
-
}
|
|
145
|
-
function findArrayPath(obj, depth = 0) {
|
|
146
|
-
if (depth > 5 || !obj || typeof obj !== 'object')
|
|
147
|
-
return null;
|
|
148
|
-
if (Array.isArray(obj)) {
|
|
149
|
-
if (obj.length >= 2 && obj.some(i => i && typeof i === 'object' && !Array.isArray(i))) {
|
|
150
|
-
return { path: '', items: obj };
|
|
151
|
-
}
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
let best = null;
|
|
155
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
156
|
-
const found = findArrayPath(val, depth + 1);
|
|
157
|
-
if (found) {
|
|
158
|
-
const fullPath = found.path ? `${key}.${found.path}` : key;
|
|
159
|
-
const candidate = { path: fullPath, items: found.items };
|
|
160
|
-
if (!best || candidate.items.length > best.items.length)
|
|
161
|
-
best = candidate;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return best;
|
|
165
|
-
}
|
|
166
|
-
function inferCapabilityName(url) {
|
|
167
|
-
const u = url.toLowerCase();
|
|
168
|
-
if (u.includes('hot') || u.includes('popular') || u.includes('ranking') || u.includes('trending'))
|
|
169
|
-
return 'hot';
|
|
170
|
-
if (u.includes('search'))
|
|
171
|
-
return 'search';
|
|
172
|
-
if (u.includes('feed') || u.includes('timeline') || u.includes('dynamic'))
|
|
173
|
-
return 'feed';
|
|
174
|
-
if (u.includes('comment') || u.includes('reply'))
|
|
175
|
-
return 'comments';
|
|
176
|
-
if (u.includes('history'))
|
|
177
|
-
return 'history';
|
|
178
|
-
if (u.includes('profile') || u.includes('me'))
|
|
179
|
-
return 'me';
|
|
180
|
-
if (u.includes('favorite') || u.includes('collect') || u.includes('bookmark'))
|
|
181
|
-
return 'favorite';
|
|
182
|
-
try {
|
|
183
|
-
const segs = new URL(url).pathname
|
|
184
|
-
.split('/')
|
|
185
|
-
.filter(s => s && !s.match(/^\d+$/) && !s.match(/^[0-9a-f]{8,}$/i) && !s.match(/^v\d+$/));
|
|
186
|
-
if (segs.length)
|
|
187
|
-
return segs[segs.length - 1].replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
188
|
-
}
|
|
189
|
-
catch { }
|
|
190
|
-
return 'data';
|
|
191
|
-
}
|
|
192
|
-
function inferStrategy(authIndicators) {
|
|
193
|
-
if (authIndicators.includes('signature'))
|
|
194
|
-
return 'intercept';
|
|
195
|
-
if (authIndicators.includes('bearer') || authIndicators.includes('csrf'))
|
|
196
|
-
return 'header';
|
|
197
|
-
return 'cookie';
|
|
198
|
-
}
|
|
199
114
|
function scoreRequest(req, arrayResult) {
|
|
200
115
|
let s = 0;
|
|
201
116
|
if (arrayResult) {
|
|
@@ -247,14 +162,7 @@ function buildRecordedYaml(site, pageUrl, req, capName, arrayResult, authIndicat
|
|
|
247
162
|
? ''
|
|
248
163
|
: itemPath.split('.').map(p => `?.${p}`).join('');
|
|
249
164
|
// Detect search/limit/page params (must be before fetch URL building to use hasSearch/hasPage)
|
|
250
|
-
const
|
|
251
|
-
try {
|
|
252
|
-
new URL(req.url).searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k))
|
|
253
|
-
qp.push(k); });
|
|
254
|
-
}
|
|
255
|
-
catch { }
|
|
256
|
-
const hasSearch = qp.some(p => SEARCH_PARAMS.has(p));
|
|
257
|
-
const hasPage = qp.some(p => PAGINATION_PARAMS.has(p));
|
|
165
|
+
const { hasSearch, hasPagination: hasPage } = classifyQueryParams(req.url);
|
|
258
166
|
// Build evaluate script
|
|
259
167
|
const mapLines = Object.entries(detectedFields)
|
|
260
168
|
.map(([role, field]) => ` ${role}: item?.${field}`)
|
|
@@ -363,10 +271,9 @@ export async function recordSession(opts) {
|
|
|
363
271
|
let stopped = false;
|
|
364
272
|
const stop = () => { stopped = true; };
|
|
365
273
|
const { promise: enterPromise, cleanup: cleanupEnter } = waitForEnter();
|
|
366
|
-
|
|
274
|
+
enterPromise.then(stop);
|
|
367
275
|
const timeoutPromise = new Promise(r => setTimeout(() => {
|
|
368
276
|
stop();
|
|
369
|
-
cleanupEnter(); // close readline to prevent process from hanging
|
|
370
277
|
r();
|
|
371
278
|
}, timeoutMs));
|
|
372
279
|
// Poll loop: drain captured data + inject interceptor into any new tabs
|
|
@@ -394,6 +301,7 @@ export async function recordSession(opts) {
|
|
|
394
301
|
}
|
|
395
302
|
}, pollMs);
|
|
396
303
|
await Promise.race([enterPromise, timeoutPromise]);
|
|
304
|
+
cleanupEnter(); // Always clean up readline to prevent process from hanging
|
|
397
305
|
clearInterval(pollInterval);
|
|
398
306
|
// Final drain from all known tabs
|
|
399
307
|
for (const tabId of injectedTabs) {
|
|
@@ -476,7 +384,7 @@ function analyzeAndWrite(site, pageUrl, requests, outDir) {
|
|
|
476
384
|
const scored = [];
|
|
477
385
|
for (const [pattern, req] of seen) {
|
|
478
386
|
const arrayResult = findArrayPath(req.body);
|
|
479
|
-
const authIndicators =
|
|
387
|
+
const authIndicators = detectAuthFromContent(req.url, req.body);
|
|
480
388
|
const score = scoreRequest(req, arrayResult);
|
|
481
389
|
if (score > 0) {
|
|
482
390
|
scored.push({ req, pattern, arrayResult, authIndicators, score });
|
package/dist/registry-api.d.ts
CHANGED
|
@@ -9,3 +9,5 @@
|
|
|
9
9
|
export { cli, Strategy, getRegistry, fullName, registerCommand } from './registry.js';
|
|
10
10
|
export type { CliCommand, Arg, CliOptions } from './registry.js';
|
|
11
11
|
export type { IPage } from './types.js';
|
|
12
|
+
export { onStartup, onBeforeExecute, onAfterExecute } from './hooks.js';
|
|
13
|
+
export type { HookFn, HookContext, HookName } from './hooks.js';
|
package/dist/registry-api.js
CHANGED
package/dist/registry.d.ts
CHANGED
|
@@ -18,6 +18,10 @@ export interface Arg {
|
|
|
18
18
|
help?: string;
|
|
19
19
|
choices?: string[];
|
|
20
20
|
}
|
|
21
|
+
export interface RequiredEnv {
|
|
22
|
+
name: string;
|
|
23
|
+
help?: string;
|
|
24
|
+
}
|
|
21
25
|
export type CommandArgs = Record<string, any>;
|
|
22
26
|
export interface CliCommand {
|
|
23
27
|
site: string;
|
|
@@ -34,6 +38,7 @@ export interface CliCommand {
|
|
|
34
38
|
/** Origin of this command: 'yaml', 'ts', or plugin name. */
|
|
35
39
|
source?: string;
|
|
36
40
|
footerExtra?: (kwargs: CommandArgs) => string | undefined;
|
|
41
|
+
requiredEnv?: RequiredEnv[];
|
|
37
42
|
/**
|
|
38
43
|
* Control pre-navigation for cookie/header context before command execution.
|
|
39
44
|
*
|
|
@@ -65,5 +70,3 @@ export declare function getRegistry(): Map<string, CliCommand>;
|
|
|
65
70
|
export declare function fullName(cmd: CliCommand): string;
|
|
66
71
|
export declare function strategyLabel(cmd: CliCommand): string;
|
|
67
72
|
export declare function registerCommand(cmd: CliCommand): void;
|
|
68
|
-
export { serializeArg, serializeCommand, formatArgSummary, formatRegistryHelpText } from './serialization.js';
|
|
69
|
-
export type { SerializedArg } from './serialization.js';
|
package/dist/registry.js
CHANGED
|
@@ -26,6 +26,7 @@ export function cli(opts) {
|
|
|
26
26
|
pipeline: opts.pipeline,
|
|
27
27
|
timeoutSeconds: opts.timeoutSeconds,
|
|
28
28
|
footerExtra: opts.footerExtra,
|
|
29
|
+
requiredEnv: opts.requiredEnv,
|
|
29
30
|
navigateBefore: opts.navigateBefore,
|
|
30
31
|
};
|
|
31
32
|
const key = fullName(cmd);
|
|
@@ -44,5 +45,3 @@ export function strategyLabel(cmd) {
|
|
|
44
45
|
export function registerCommand(cmd) {
|
|
45
46
|
_registry.set(fullName(cmd), cmd);
|
|
46
47
|
}
|
|
47
|
-
// Re-export serialization helpers from their dedicated module
|
|
48
|
-
export { serializeArg, serializeCommand, formatArgSummary, formatRegistryHelpText } from './serialization.js';
|
package/dist/runtime.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export declare function getBrowserFactory(): new () => IBrowserFactory;
|
|
|
7
7
|
export declare const DEFAULT_BROWSER_CONNECT_TIMEOUT: number;
|
|
8
8
|
export declare const DEFAULT_BROWSER_COMMAND_TIMEOUT: number;
|
|
9
9
|
export declare const DEFAULT_BROWSER_EXPLORE_TIMEOUT: number;
|
|
10
|
-
export declare const DEFAULT_BROWSER_SMOKE_TIMEOUT: number;
|
|
11
10
|
/**
|
|
12
11
|
* Timeout with seconds unit. Used for high-level command timeouts.
|
|
13
12
|
*/
|
package/dist/runtime.js
CHANGED
|
@@ -7,10 +7,20 @@ import { TimeoutError } from './errors.js';
|
|
|
7
7
|
export function getBrowserFactory() {
|
|
8
8
|
return (process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge);
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
function parseEnvTimeout(envVar, fallback) {
|
|
11
|
+
const raw = process.env[envVar];
|
|
12
|
+
if (raw === undefined)
|
|
13
|
+
return fallback;
|
|
14
|
+
const parsed = parseInt(raw, 10);
|
|
15
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
16
|
+
console.error(`[runtime] Invalid ${envVar}="${raw}", using default ${fallback}s`);
|
|
17
|
+
return fallback;
|
|
18
|
+
}
|
|
19
|
+
return parsed;
|
|
20
|
+
}
|
|
21
|
+
export const DEFAULT_BROWSER_CONNECT_TIMEOUT = parseEnvTimeout('OPENCLI_BROWSER_CONNECT_TIMEOUT', 30);
|
|
22
|
+
export const DEFAULT_BROWSER_COMMAND_TIMEOUT = parseEnvTimeout('OPENCLI_BROWSER_COMMAND_TIMEOUT', 60);
|
|
23
|
+
export const DEFAULT_BROWSER_EXPLORE_TIMEOUT = parseEnvTimeout('OPENCLI_BROWSER_EXPLORE_TIMEOUT', 120);
|
|
14
24
|
/**
|
|
15
25
|
* Timeout with seconds unit. Used for high-level command timeouts.
|
|
16
26
|
*/
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Aria snapshot formatter: parses Playwright MCP snapshot text into clean format.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 1. Parse & filter: strip annotations, metadata, noise
|
|
6
|
-
* 2. Deduplicate: generic/text
|
|
7
|
-
* 3.
|
|
8
|
-
* 4.
|
|
9
|
-
* 5. Prune: empty containers (iterative bottom-up)
|
|
10
|
-
* 6. Collapse: single-child containers
|
|
4
|
+
* 4-pass pipeline:
|
|
5
|
+
* 1. Parse & filter: strip annotations, metadata, noise, ads, boilerplate subtrees
|
|
6
|
+
* 2. Deduplicate: generic/text parent match, heading+link, nested identical links
|
|
7
|
+
* 3. Prune: empty containers (iterative bottom-up)
|
|
8
|
+
* 4. Collapse: single-child containers
|
|
11
9
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
compact?: boolean;
|
|
15
|
-
maxDepth?: number;
|
|
16
|
-
maxTextLength?: number;
|
|
17
|
-
}
|
|
18
|
-
export declare function formatSnapshot(raw: string, opts?: FormatOptions): string;
|
|
10
|
+
import type { SnapshotOptions } from './types.js';
|
|
11
|
+
export declare function formatSnapshot(raw: string, opts?: SnapshotOptions): string;
|