@jackwener/opencli 1.3.3 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/setup-chrome/action.yml +5 -4
- package/.github/pull_request_template.md +3 -1
- package/.github/workflows/build-extension.yml +7 -1
- package/.github/workflows/ci.yml +46 -6
- package/.github/workflows/docs.yml +1 -1
- package/.github/workflows/e2e-headed.yml +36 -3
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/security.yml +0 -3
- package/CHANGELOG.md +78 -0
- package/CONTRIBUTING.md +6 -3
- package/PRIVACY.md +57 -0
- package/README.md +31 -4
- package/README.zh-CN.md +31 -4
- package/SKILL.md +107 -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 +53 -41
- package/dist/browser/cdp.test.d.ts +1 -0
- package/dist/browser/cdp.test.js +52 -0
- 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/index.d.ts +2 -2
- package/dist/browser/index.js +1 -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 +34 -37
- package/dist/browser/stealth.d.ts +0 -2
- package/dist/browser/stealth.js +24 -9
- package/dist/browser.test.js +2 -2
- 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 +1325 -256
- 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/apple-podcasts/search.js +2 -1
- package/dist/clis/arxiv/search.js +3 -3
- package/dist/clis/bbc/news.js +0 -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/ctrip/search.js +0 -1
- 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/douyin/_shared/browser-fetch.d.ts +10 -0
- package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
- package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.js +5 -0
- package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
- package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
- package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
- package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
- package/dist/clis/douyin/_shared/sts2.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
- package/dist/clis/douyin/_shared/text-extra.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
- package/dist/clis/douyin/_shared/timing.d.ts +2 -0
- package/dist/clis/douyin/_shared/timing.js +22 -0
- package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/timing.test.js +28 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
- package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
- package/dist/clis/douyin/_shared/tos-upload.js +295 -0
- package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
- package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
- package/dist/clis/douyin/_shared/transcode.js +45 -0
- package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/transcode.test.js +93 -0
- package/dist/clis/douyin/_shared/types.d.ts +26 -0
- package/dist/clis/douyin/_shared/types.js +1 -0
- package/dist/clis/douyin/activities.d.ts +1 -0
- package/dist/clis/douyin/activities.js +20 -0
- package/dist/clis/douyin/activities.test.d.ts +1 -0
- package/dist/clis/douyin/activities.test.js +22 -0
- package/dist/clis/douyin/collections.d.ts +1 -0
- package/dist/clis/douyin/collections.js +22 -0
- package/dist/clis/douyin/collections.test.d.ts +1 -0
- package/dist/clis/douyin/collections.test.js +23 -0
- package/dist/clis/douyin/delete.d.ts +1 -0
- package/dist/clis/douyin/delete.js +18 -0
- package/dist/clis/douyin/delete.test.d.ts +1 -0
- package/dist/clis/douyin/delete.test.js +11 -0
- package/dist/clis/douyin/draft.d.ts +14 -0
- package/dist/clis/douyin/draft.js +237 -0
- package/dist/clis/douyin/draft.test.d.ts +1 -0
- package/dist/clis/douyin/draft.test.js +11 -0
- package/dist/clis/douyin/drafts.d.ts +1 -0
- package/dist/clis/douyin/drafts.js +23 -0
- package/dist/clis/douyin/drafts.test.d.ts +1 -0
- package/dist/clis/douyin/drafts.test.js +11 -0
- package/dist/clis/douyin/hashtag.d.ts +1 -0
- package/dist/clis/douyin/hashtag.js +45 -0
- package/dist/clis/douyin/hashtag.test.d.ts +1 -0
- package/dist/clis/douyin/hashtag.test.js +25 -0
- package/dist/clis/douyin/location.d.ts +1 -0
- package/dist/clis/douyin/location.js +24 -0
- package/dist/clis/douyin/location.test.d.ts +1 -0
- package/dist/clis/douyin/location.test.js +23 -0
- package/dist/clis/douyin/profile.d.ts +1 -0
- package/dist/clis/douyin/profile.js +28 -0
- package/dist/clis/douyin/profile.test.d.ts +1 -0
- package/dist/clis/douyin/profile.test.js +11 -0
- package/dist/clis/douyin/publish.d.ts +14 -0
- package/dist/clis/douyin/publish.js +288 -0
- package/dist/clis/douyin/publish.test.d.ts +1 -0
- package/dist/clis/douyin/publish.test.js +38 -0
- package/dist/clis/douyin/stats.d.ts +1 -0
- package/dist/clis/douyin/stats.js +27 -0
- package/dist/clis/douyin/stats.test.d.ts +1 -0
- package/dist/clis/douyin/stats.test.js +22 -0
- package/dist/clis/douyin/update.d.ts +1 -0
- package/dist/clis/douyin/update.js +31 -0
- package/dist/clis/douyin/update.test.d.ts +1 -0
- package/dist/clis/douyin/update.test.js +11 -0
- package/dist/clis/douyin/videos.d.ts +1 -0
- package/dist/clis/douyin/videos.js +34 -0
- package/dist/clis/douyin/videos.test.d.ts +1 -0
- package/dist/clis/douyin/videos.test.js +11 -0
- 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/hackernews/search.yaml +1 -1
- package/dist/clis/instagram/search.yaml +2 -1
- 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/linux-do/search.yaml +3 -1
- package/dist/clis/medium/feed.js +1 -1
- package/dist/clis/medium/search.js +2 -2
- 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/reuters/search.js +0 -1
- 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 +32 -13
- package/dist/clis/twitter/search.test.d.ts +1 -0
- package/dist/clis/twitter/search.test.js +156 -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/weibo/comments.d.ts +1 -0
- package/dist/clis/weibo/comments.js +53 -0
- package/dist/clis/weibo/feed.d.ts +1 -0
- package/dist/clis/weibo/feed.js +56 -0
- package/dist/clis/weibo/hot.js +0 -1
- package/dist/clis/weibo/me.d.ts +1 -0
- package/dist/clis/weibo/me.js +76 -0
- package/dist/clis/weibo/post.d.ts +1 -0
- package/dist/clis/weibo/post.js +75 -0
- package/dist/clis/weibo/user.d.ts +1 -0
- package/dist/clis/weibo/user.js +63 -0
- package/dist/clis/weibo/utils.d.ts +6 -0
- package/dist/clis/weibo/utils.js +30 -0
- package/dist/clis/weread/search.js +3 -2
- 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/xueqiu/search.yaml +2 -1
- package/dist/clis/yahoo-finance/quote.js +0 -1
- package/dist/clis/youtube/channel.d.ts +1 -0
- package/dist/clis/youtube/channel.js +150 -0
- package/dist/clis/youtube/comments.d.ts +1 -0
- package/dist/clis/youtube/comments.js +95 -0
- package/dist/clis/youtube/search.js +0 -1
- package/dist/clis/youtube/transcript.js +5 -4
- package/dist/clis/youtube/video.js +3 -2
- package/dist/clis/zhihu/search.yaml +2 -1
- package/dist/daemon.js +7 -3
- package/dist/discovery.js +11 -10
- package/dist/doctor.js +2 -1
- 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 -25
- 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/weread-search-regression.test.d.ts +1 -0
- package/dist/weread-search-regression.test.js +39 -0
- package/dist/yaml-schema.d.ts +26 -0
- package/dist/yaml-schema.js +5 -0
- package/docs/.vitepress/config.mts +16 -0
- package/docs/adapters/browser/dictionary.md +27 -0
- package/docs/adapters/browser/douyin.md +75 -0
- 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/twitter.md +6 -0
- package/docs/adapters/browser/web.md +30 -0
- package/docs/adapters/browser/xueqiu.md +27 -9
- package/docs/adapters/index.md +9 -2
- 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 +100 -35
- package/extension/manifest.json +6 -2
- package/extension/package.json +1 -1
- package/extension/popup.html +84 -0
- package/extension/popup.js +25 -0
- package/extension/src/background.test.ts +46 -1
- package/extension/src/background.ts +128 -34
- 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 +59 -44
- package/src/browser/dom-snapshot.test.ts +42 -0
- package/src/browser/dom-snapshot.ts +56 -3
- package/src/browser/index.ts +2 -2
- package/src/browser/mcp.ts +2 -4
- package/src/browser/page.ts +34 -37
- package/src/browser/stealth.ts +24 -10
- package/src/browser.test.ts +2 -2
- package/src/build-manifest.test.ts +14 -0
- package/src/build-manifest.ts +13 -31
- 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/apple-podcasts/search.ts +2 -1
- package/src/clis/arxiv/search.ts +3 -3
- package/src/clis/bbc/news.ts +0 -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/ctrip/search.ts +0 -1
- 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/douyin/_shared/browser-fetch.test.ts +38 -0
- package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
- package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
- package/src/clis/douyin/_shared/creation-id.ts +8 -0
- package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
- package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
- package/src/clis/douyin/_shared/sts2.ts +20 -0
- package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
- package/src/clis/douyin/_shared/text-extra.ts +33 -0
- package/src/clis/douyin/_shared/timing.test.ts +38 -0
- package/src/clis/douyin/_shared/timing.ts +22 -0
- package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
- package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
- package/src/clis/douyin/_shared/tos-upload.ts +444 -0
- package/src/clis/douyin/_shared/transcode.test.ts +117 -0
- package/src/clis/douyin/_shared/transcode.ts +78 -0
- package/src/clis/douyin/_shared/types.ts +29 -0
- package/src/clis/douyin/activities.test.ts +25 -0
- package/src/clis/douyin/activities.ts +23 -0
- package/src/clis/douyin/collections.test.ts +26 -0
- package/src/clis/douyin/collections.ts +25 -0
- package/src/clis/douyin/delete.test.ts +12 -0
- package/src/clis/douyin/delete.ts +20 -0
- package/src/clis/douyin/draft.test.ts +12 -0
- package/src/clis/douyin/draft.ts +282 -0
- package/src/clis/douyin/drafts.test.ts +12 -0
- package/src/clis/douyin/drafts.ts +27 -0
- package/src/clis/douyin/hashtag.test.ts +28 -0
- package/src/clis/douyin/hashtag.ts +56 -0
- package/src/clis/douyin/location.test.ts +26 -0
- package/src/clis/douyin/location.ts +27 -0
- package/src/clis/douyin/profile.test.ts +12 -0
- package/src/clis/douyin/profile.ts +37 -0
- package/src/clis/douyin/publish.test.ts +45 -0
- package/src/clis/douyin/publish.ts +340 -0
- package/src/clis/douyin/stats.test.ts +25 -0
- package/src/clis/douyin/stats.ts +30 -0
- package/src/clis/douyin/update.test.ts +12 -0
- package/src/clis/douyin/update.ts +43 -0
- package/src/clis/douyin/videos.test.ts +12 -0
- package/src/clis/douyin/videos.ts +49 -0
- package/src/clis/grok/ask.test.ts +25 -0
- package/src/clis/grok/ask.ts +25 -12
- package/src/clis/hackernews/search.yaml +1 -1
- package/src/clis/instagram/search.yaml +2 -1
- 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/linux-do/search.yaml +3 -1
- package/src/clis/medium/feed.ts +1 -1
- package/src/clis/medium/search.ts +2 -2
- 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/reuters/search.ts +0 -1
- 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 +180 -0
- package/src/clis/twitter/search.ts +40 -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/weibo/comments.ts +54 -0
- package/src/clis/weibo/feed.ts +57 -0
- package/src/clis/weibo/hot.ts +0 -1
- package/src/clis/weibo/me.ts +77 -0
- package/src/clis/weibo/post.ts +77 -0
- package/src/clis/weibo/user.ts +64 -0
- package/src/clis/weibo/utils.ts +32 -0
- package/src/clis/weread/search.ts +3 -2
- 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/xueqiu/search.yaml +2 -1
- package/src/clis/yahoo-finance/quote.ts +0 -1
- package/src/clis/youtube/channel.ts +155 -0
- package/src/clis/youtube/comments.ts +97 -0
- package/src/clis/youtube/search.ts +0 -1
- package/src/clis/youtube/transcript.ts +5 -4
- package/src/clis/youtube/video.ts +3 -2
- package/src/clis/zhihu/search.yaml +2 -1
- package/src/daemon.ts +5 -4
- package/src/discovery.ts +12 -34
- package/src/doctor.ts +3 -2
- 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 -25
- 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 -5
- package/src/weread-search-regression.test.ts +44 -0
- package/src/yaml-schema.ts +28 -0
- package/tests/e2e/browser-auth.test.ts +25 -0
- package/tests/e2e/browser-public-extended.test.ts +162 -0
- package/tests/e2e/browser-public.test.ts +7 -146
- package/tests/e2e/plugin-management.test.ts +137 -0
- package/tests/e2e/public-commands.test.ts +34 -1
- package/vitest.config.ts +33 -8
- 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/plugin.ts
CHANGED
|
@@ -6,16 +6,120 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import * as fs from 'node:fs';
|
|
9
|
+
import * as os from 'node:os';
|
|
9
10
|
import * as path from 'node:path';
|
|
10
11
|
import { execSync, execFileSync } from 'node:child_process';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
11
13
|
import { PLUGINS_DIR } from './discovery.js';
|
|
14
|
+
import { getErrorMessage } from './errors.js';
|
|
12
15
|
import { log } from './logger.js';
|
|
13
16
|
|
|
17
|
+
const isWindows = process.platform === 'win32';
|
|
18
|
+
|
|
19
|
+
/** Get home directory, respecting HOME environment variable for test isolation. */
|
|
20
|
+
function getHomeDir(): string {
|
|
21
|
+
return process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Path to the lock file that tracks installed plugin versions. */
|
|
25
|
+
export function getLockFilePath(): string {
|
|
26
|
+
return path.join(getHomeDir(), '.opencli', 'plugins.lock.json');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Legacy const for backward compatibility (computed at load time)
|
|
30
|
+
export const LOCK_FILE = path.join(os.homedir(), '.opencli', 'plugins.lock.json');
|
|
31
|
+
|
|
32
|
+
export interface LockEntry {
|
|
33
|
+
source: string;
|
|
34
|
+
commitHash: string;
|
|
35
|
+
installedAt: string;
|
|
36
|
+
updatedAt?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
14
39
|
export interface PluginInfo {
|
|
15
40
|
name: string;
|
|
16
41
|
path: string;
|
|
17
42
|
commands: string[];
|
|
18
43
|
source?: string;
|
|
44
|
+
version?: string;
|
|
45
|
+
installedAt?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Validation helpers ──────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
export interface ValidationResult {
|
|
51
|
+
valid: boolean;
|
|
52
|
+
errors: string[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Lock file helpers ───────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
export function readLockFile(): Record<string, LockEntry> {
|
|
58
|
+
try {
|
|
59
|
+
const raw = fs.readFileSync(getLockFilePath(), 'utf-8');
|
|
60
|
+
return JSON.parse(raw) as Record<string, LockEntry>;
|
|
61
|
+
} catch {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function writeLockFile(lock: Record<string, LockEntry>): void {
|
|
67
|
+
const lockPath = getLockFilePath();
|
|
68
|
+
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
69
|
+
fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Get the HEAD commit hash of a git repo directory. */
|
|
73
|
+
export function getCommitHash(dir: string): string | undefined {
|
|
74
|
+
try {
|
|
75
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
76
|
+
cwd: dir,
|
|
77
|
+
encoding: 'utf-8',
|
|
78
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
79
|
+
}).trim();
|
|
80
|
+
} catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate that a downloaded plugin directory is a structurally valid plugin.
|
|
87
|
+
* Checks for at least one command file (.yaml, .yml, .ts, .js) and a valid
|
|
88
|
+
* package.json if it contains .ts files.
|
|
89
|
+
*/
|
|
90
|
+
export function validatePluginStructure(pluginDir: string): ValidationResult {
|
|
91
|
+
const errors: string[] = [];
|
|
92
|
+
|
|
93
|
+
if (!fs.existsSync(pluginDir)) {
|
|
94
|
+
return { valid: false, errors: ['Plugin directory does not exist'] };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const files = fs.readdirSync(pluginDir);
|
|
98
|
+
const hasYaml = files.some(f => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
99
|
+
const hasTs = files.some(f => f.endsWith('.ts') && !f.endsWith('.d.ts') && !f.endsWith('.test.ts'));
|
|
100
|
+
const hasJs = files.some(f => f.endsWith('.js') && !f.endsWith('.d.js'));
|
|
101
|
+
|
|
102
|
+
if (!hasYaml && !hasTs && !hasJs) {
|
|
103
|
+
errors.push(`No command files found in plugin directory. A plugin must contain at least one .yaml, .ts, or .js command file.`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (hasTs) {
|
|
107
|
+
const pkgJsonPath = path.join(pluginDir, 'package.json');
|
|
108
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
109
|
+
errors.push(`Plugin contains .ts files but no package.json. A package.json with "type": "module" and "@jackwener/opencli" peer dependency is required for TS plugins.`);
|
|
110
|
+
} else {
|
|
111
|
+
try {
|
|
112
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
113
|
+
if (pkg.type !== 'module') {
|
|
114
|
+
errors.push(`Plugin package.json must have "type": "module" for TypeScript plugins.`);
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
errors.push(`Plugin package.json is malformed or invalid JSON.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { valid: errors.length === 0, errors };
|
|
19
123
|
}
|
|
20
124
|
|
|
21
125
|
/**
|
|
@@ -31,9 +135,10 @@ function postInstallLifecycle(pluginDir: string): void {
|
|
|
31
135
|
cwd: pluginDir,
|
|
32
136
|
encoding: 'utf-8',
|
|
33
137
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
138
|
+
...(isWindows && { shell: true }),
|
|
34
139
|
});
|
|
35
|
-
} catch {
|
|
36
|
-
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error(`[plugin] npm install failed in ${pluginDir}: ${err instanceof Error ? err.message : err}`);
|
|
37
142
|
}
|
|
38
143
|
|
|
39
144
|
// Symlink host opencli so TS plugins resolve '@jackwener/opencli/registry'
|
|
@@ -74,11 +179,30 @@ export function installPlugin(source: string): string {
|
|
|
74
179
|
encoding: 'utf-8',
|
|
75
180
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
76
181
|
});
|
|
77
|
-
} catch (err
|
|
78
|
-
throw new Error(`Failed to clone plugin: ${err
|
|
182
|
+
} catch (err) {
|
|
183
|
+
throw new Error(`Failed to clone plugin: ${getErrorMessage(err)}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const validation = validatePluginStructure(targetDir);
|
|
187
|
+
if (!validation.valid) {
|
|
188
|
+
// If validation fails, clean up the cloned directory and abort
|
|
189
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
190
|
+
throw new Error(`Invalid plugin structure:\n- ${validation.errors.join('\n- ')}`);
|
|
79
191
|
}
|
|
80
192
|
|
|
81
193
|
postInstallLifecycle(targetDir);
|
|
194
|
+
|
|
195
|
+
const commitHash = getCommitHash(targetDir);
|
|
196
|
+
if (commitHash) {
|
|
197
|
+
const lock = readLockFile();
|
|
198
|
+
lock[name] = {
|
|
199
|
+
source: cloneUrl,
|
|
200
|
+
commitHash,
|
|
201
|
+
installedAt: new Date().toISOString(),
|
|
202
|
+
};
|
|
203
|
+
writeLockFile(lock);
|
|
204
|
+
}
|
|
205
|
+
|
|
82
206
|
return name;
|
|
83
207
|
}
|
|
84
208
|
|
|
@@ -91,6 +215,12 @@ export function uninstallPlugin(name: string): void {
|
|
|
91
215
|
throw new Error(`Plugin "${name}" is not installed.`);
|
|
92
216
|
}
|
|
93
217
|
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
218
|
+
|
|
219
|
+
const lock = readLockFile();
|
|
220
|
+
if (lock[name]) {
|
|
221
|
+
delete lock[name];
|
|
222
|
+
writeLockFile(lock);
|
|
223
|
+
}
|
|
94
224
|
}
|
|
95
225
|
|
|
96
226
|
/**
|
|
@@ -108,11 +238,54 @@ export function updatePlugin(name: string): void {
|
|
|
108
238
|
encoding: 'utf-8',
|
|
109
239
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
110
240
|
});
|
|
111
|
-
} catch (err
|
|
112
|
-
throw new Error(`Failed to update plugin: ${err
|
|
241
|
+
} catch (err) {
|
|
242
|
+
throw new Error(`Failed to update plugin: ${getErrorMessage(err)}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const validation = validatePluginStructure(targetDir);
|
|
246
|
+
if (!validation.valid) {
|
|
247
|
+
log.warn(`Plugin "${name}" updated, but structure is now invalid:\n- ${validation.errors.join('\n- ')}`);
|
|
113
248
|
}
|
|
114
249
|
|
|
115
250
|
postInstallLifecycle(targetDir);
|
|
251
|
+
|
|
252
|
+
const commitHash = getCommitHash(targetDir);
|
|
253
|
+
if (commitHash) {
|
|
254
|
+
const lock = readLockFile();
|
|
255
|
+
const existing = lock[name];
|
|
256
|
+
lock[name] = {
|
|
257
|
+
source: existing?.source ?? getPluginSource(targetDir) ?? '',
|
|
258
|
+
commitHash,
|
|
259
|
+
installedAt: existing?.installedAt ?? new Date().toISOString(),
|
|
260
|
+
updatedAt: new Date().toISOString(),
|
|
261
|
+
};
|
|
262
|
+
writeLockFile(lock);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface UpdateResult {
|
|
267
|
+
name: string;
|
|
268
|
+
success: boolean;
|
|
269
|
+
error?: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Update all installed plugins.
|
|
274
|
+
* Continues even if individual plugin updates fail.
|
|
275
|
+
*/
|
|
276
|
+
export function updateAllPlugins(): UpdateResult[] {
|
|
277
|
+
return listPlugins().map((plugin): UpdateResult => {
|
|
278
|
+
try {
|
|
279
|
+
updatePlugin(plugin.name);
|
|
280
|
+
return { name: plugin.name, success: true };
|
|
281
|
+
} catch (err) {
|
|
282
|
+
return {
|
|
283
|
+
name: plugin.name,
|
|
284
|
+
success: false,
|
|
285
|
+
error: getErrorMessage(err),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
});
|
|
116
289
|
}
|
|
117
290
|
|
|
118
291
|
/**
|
|
@@ -122,6 +295,7 @@ export function listPlugins(): PluginInfo[] {
|
|
|
122
295
|
if (!fs.existsSync(PLUGINS_DIR)) return [];
|
|
123
296
|
|
|
124
297
|
const entries = fs.readdirSync(PLUGINS_DIR, { withFileTypes: true });
|
|
298
|
+
const lock = readLockFile();
|
|
125
299
|
const plugins: PluginInfo[] = [];
|
|
126
300
|
|
|
127
301
|
for (const entry of entries) {
|
|
@@ -129,12 +303,15 @@ export function listPlugins(): PluginInfo[] {
|
|
|
129
303
|
const pluginDir = path.join(PLUGINS_DIR, entry.name);
|
|
130
304
|
const commands = scanPluginCommands(pluginDir);
|
|
131
305
|
const source = getPluginSource(pluginDir);
|
|
306
|
+
const lockEntry = lock[entry.name];
|
|
132
307
|
|
|
133
308
|
plugins.push({
|
|
134
309
|
name: entry.name,
|
|
135
310
|
path: pluginDir,
|
|
136
311
|
commands,
|
|
137
312
|
source,
|
|
313
|
+
version: lockEntry?.commitHash?.slice(0, 7),
|
|
314
|
+
installedAt: lockEntry?.installedAt,
|
|
138
315
|
});
|
|
139
316
|
}
|
|
140
317
|
|
|
@@ -163,7 +340,7 @@ function scanPluginCommands(dir: string): string[] {
|
|
|
163
340
|
/** Get git remote origin URL */
|
|
164
341
|
function getPluginSource(dir: string): string | undefined {
|
|
165
342
|
try {
|
|
166
|
-
return
|
|
343
|
+
return execFileSync('git', ['config', '--get', 'remote.origin.url'], {
|
|
167
344
|
cwd: dir,
|
|
168
345
|
encoding: 'utf-8',
|
|
169
346
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -210,7 +387,7 @@ function linkHostOpencli(pluginDir: string): void {
|
|
|
210
387
|
// Determine the host opencli package root from this module's location.
|
|
211
388
|
// Both dev (tsx src/plugin.ts) and prod (node dist/plugin.js) are one level
|
|
212
389
|
// deep, so path.dirname + '..' always gives us the package root.
|
|
213
|
-
const thisFile =
|
|
390
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
214
391
|
const hostRoot = path.resolve(path.dirname(thisFile), '..');
|
|
215
392
|
|
|
216
393
|
const targetLink = path.join(pluginDir, 'node_modules', '@jackwener', 'opencli');
|
|
@@ -223,27 +400,85 @@ function linkHostOpencli(pluginDir: string): void {
|
|
|
223
400
|
// Ensure parent directory exists
|
|
224
401
|
fs.mkdirSync(path.dirname(targetLink), { recursive: true });
|
|
225
402
|
|
|
226
|
-
//
|
|
227
|
-
|
|
403
|
+
// Use 'junction' on Windows (doesn't require admin privileges),
|
|
404
|
+
// 'dir' symlink on other platforms.
|
|
405
|
+
const linkType = isWindows ? 'junction' : 'dir';
|
|
406
|
+
fs.symlinkSync(hostRoot, targetLink, linkType);
|
|
228
407
|
log.debug(`Linked host opencli into plugin: ${targetLink} → ${hostRoot}`);
|
|
229
|
-
} catch (err
|
|
230
|
-
log.warn(`Failed to link host opencli into plugin: ${err
|
|
408
|
+
} catch (err) {
|
|
409
|
+
log.warn(`Failed to link host opencli into plugin: ${getErrorMessage(err)}`);
|
|
231
410
|
}
|
|
232
411
|
}
|
|
233
412
|
|
|
413
|
+
/**
|
|
414
|
+
* Resolve the path to the esbuild CLI executable with fallback strategies.
|
|
415
|
+
*/
|
|
416
|
+
export function resolveEsbuildBin(): string | null {
|
|
417
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
418
|
+
const hostRoot = path.resolve(path.dirname(thisFile), '..');
|
|
419
|
+
|
|
420
|
+
// Strategy 1 (Windows): prefer the .cmd wrapper which is executable via shell
|
|
421
|
+
if (isWindows) {
|
|
422
|
+
const cmdPath = path.join(hostRoot, 'node_modules', '.bin', 'esbuild.cmd');
|
|
423
|
+
if (fs.existsSync(cmdPath)) {
|
|
424
|
+
return cmdPath;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Strategy 2: resolve esbuild binary via import.meta.resolve
|
|
429
|
+
// (On Unix, shebang scripts are directly executable; on Windows they are not,
|
|
430
|
+
// so this strategy is skipped on Windows in favour of the .cmd wrapper above.)
|
|
431
|
+
if (!isWindows) {
|
|
432
|
+
try {
|
|
433
|
+
const pkgUrl = import.meta.resolve('esbuild/package.json');
|
|
434
|
+
if (pkgUrl.startsWith('file://')) {
|
|
435
|
+
const pkgPath = fileURLToPath(pkgUrl);
|
|
436
|
+
const pkgRaw = fs.readFileSync(pkgPath, 'utf8');
|
|
437
|
+
const pkg = JSON.parse(pkgRaw);
|
|
438
|
+
if (pkg.bin && typeof pkg.bin === 'object' && pkg.bin.esbuild) {
|
|
439
|
+
const binPath = path.resolve(path.dirname(pkgPath), pkg.bin.esbuild);
|
|
440
|
+
if (fs.existsSync(binPath)) return binPath;
|
|
441
|
+
} else if (typeof pkg.bin === 'string') {
|
|
442
|
+
const binPath = path.resolve(path.dirname(pkgPath), pkg.bin);
|
|
443
|
+
if (fs.existsSync(binPath)) return binPath;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
} catch {
|
|
447
|
+
// ignore package resolution failures
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Strategy 3: fallback to node_modules/.bin/esbuild (Unix)
|
|
452
|
+
const binFallback = path.join(hostRoot, 'node_modules', '.bin', 'esbuild');
|
|
453
|
+
if (fs.existsSync(binFallback)) {
|
|
454
|
+
return binFallback;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Strategy 4: global esbuild in PATH
|
|
458
|
+
try {
|
|
459
|
+
const lookupCmd = isWindows ? 'where esbuild' : 'which esbuild';
|
|
460
|
+
// `where` on Windows may return multiple lines; take only the first match.
|
|
461
|
+
const globalBin = execSync(lookupCmd, { encoding: 'utf-8', stdio: 'pipe' }).trim().split('\n')[0].trim();
|
|
462
|
+
if (globalBin && fs.existsSync(globalBin)) {
|
|
463
|
+
return globalBin;
|
|
464
|
+
}
|
|
465
|
+
} catch {
|
|
466
|
+
// ignore PATH lookup failures
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
|
|
234
472
|
/**
|
|
235
473
|
* Transpile TS plugin files to JS so they work in production mode.
|
|
236
474
|
* Uses esbuild from the host opencli's node_modules for fast single-file transpilation.
|
|
237
475
|
*/
|
|
238
476
|
function transpilePluginTs(pluginDir: string): void {
|
|
239
477
|
try {
|
|
240
|
-
|
|
241
|
-
const thisFile = new URL(import.meta.url).pathname;
|
|
242
|
-
const hostRoot = path.resolve(path.dirname(thisFile), '..');
|
|
243
|
-
const esbuildBin = path.join(hostRoot, 'node_modules', '.bin', 'esbuild');
|
|
478
|
+
const esbuildBin = resolveEsbuildBin();
|
|
244
479
|
|
|
245
|
-
if (!
|
|
246
|
-
log.debug('esbuild not found in host node_modules, skipping TS transpilation');
|
|
480
|
+
if (!esbuildBin) {
|
|
481
|
+
log.debug('esbuild not found in host node_modules, via resolve, or in PATH, skipping TS transpilation');
|
|
247
482
|
return;
|
|
248
483
|
}
|
|
249
484
|
|
|
@@ -264,10 +499,11 @@ function transpilePluginTs(pluginDir: string): void {
|
|
|
264
499
|
cwd: pluginDir,
|
|
265
500
|
encoding: 'utf-8',
|
|
266
501
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
502
|
+
...(isWindows && { shell: true }),
|
|
267
503
|
});
|
|
268
504
|
log.debug(`Transpiled plugin file: ${tsFile} → ${jsFile}`);
|
|
269
|
-
} catch (err
|
|
270
|
-
log.warn(`Failed to transpile ${tsFile}: ${err
|
|
505
|
+
} catch (err) {
|
|
506
|
+
log.warn(`Failed to transpile ${tsFile}: ${getErrorMessage(err)}`);
|
|
271
507
|
}
|
|
272
508
|
}
|
|
273
509
|
} catch {
|
|
@@ -275,4 +511,12 @@ function transpilePluginTs(pluginDir: string): void {
|
|
|
275
511
|
}
|
|
276
512
|
}
|
|
277
513
|
|
|
278
|
-
export {
|
|
514
|
+
export {
|
|
515
|
+
resolveEsbuildBin as _resolveEsbuildBin,
|
|
516
|
+
getCommitHash as _getCommitHash,
|
|
517
|
+
parseSource as _parseSource,
|
|
518
|
+
readLockFile as _readLockFile,
|
|
519
|
+
updateAllPlugins as _updateAllPlugins,
|
|
520
|
+
validatePluginStructure as _validatePluginStructure,
|
|
521
|
+
writeLockFile as _writeLockFile,
|
|
522
|
+
};
|
package/src/record.ts
CHANGED
|
@@ -19,12 +19,15 @@ import chalk from 'chalk';
|
|
|
19
19
|
import yaml from 'js-yaml';
|
|
20
20
|
import { sendCommand } from './browser/daemon-client.js';
|
|
21
21
|
import type { IPage } from './types.js';
|
|
22
|
+
import { SEARCH_PARAMS, PAGINATION_PARAMS, FIELD_ROLES } from './constants.js';
|
|
22
23
|
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
urlToPattern,
|
|
25
|
+
findArrayPath,
|
|
26
|
+
inferCapabilityName,
|
|
27
|
+
inferStrategy,
|
|
28
|
+
detectAuthFromContent,
|
|
29
|
+
classifyQueryParams,
|
|
30
|
+
} from './analysis.js';
|
|
28
31
|
|
|
29
32
|
// ── Types ──────────────────────────────────────────────────────────────────
|
|
30
33
|
|
|
@@ -142,78 +145,6 @@ function generateReadRecordedJs(): string {
|
|
|
142
145
|
|
|
143
146
|
// ── Analysis helpers ───────────────────────────────────────────────────────
|
|
144
147
|
|
|
145
|
-
function urlToPattern(url: string): string {
|
|
146
|
-
try {
|
|
147
|
-
const p = new URL(url);
|
|
148
|
-
const pathNorm = p.pathname
|
|
149
|
-
.replace(/\/\d+/g, '/{id}')
|
|
150
|
-
.replace(/\/[0-9a-fA-F]{8,}/g, '/{hex}')
|
|
151
|
-
.replace(/\/BV[a-zA-Z0-9]{10}/g, '/{bvid}');
|
|
152
|
-
const params: string[] = [];
|
|
153
|
-
p.searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k)) params.push(k); });
|
|
154
|
-
return `${p.host}${pathNorm}${params.length ? '?' + params.sort().map(k => `${k}={}`).join('&') : ''}`;
|
|
155
|
-
} catch { return url; }
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function detectAuthIndicators(url: string, body: unknown): string[] {
|
|
159
|
-
const indicators: string[] = [];
|
|
160
|
-
// Heuristic: if body contains sign/w_rid fields, it's likely signed
|
|
161
|
-
if (body && typeof body === 'object') {
|
|
162
|
-
const keys = Object.keys(body as object).map(k => k.toLowerCase());
|
|
163
|
-
if (keys.some(k => k.includes('sign') || k === 'w_rid' || k.includes('token'))) {
|
|
164
|
-
indicators.push('signature');
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// Check URL for common auth patterns
|
|
168
|
-
if (url.includes('/wbi/') || url.includes('w_rid=')) indicators.push('signature');
|
|
169
|
-
if (url.includes('bearer') || url.includes('access_token')) indicators.push('bearer');
|
|
170
|
-
return indicators;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function findArrayPath(obj: unknown, depth = 0): { path: string; items: unknown[] } | null {
|
|
174
|
-
if (depth > 5 || !obj || typeof obj !== 'object') return null;
|
|
175
|
-
if (Array.isArray(obj)) {
|
|
176
|
-
if (obj.length >= 2 && obj.some(i => i && typeof i === 'object' && !Array.isArray(i))) {
|
|
177
|
-
return { path: '', items: obj };
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
let best: { path: string; items: unknown[] } | null = null;
|
|
182
|
-
for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {
|
|
183
|
-
const found = findArrayPath(val, depth + 1);
|
|
184
|
-
if (found) {
|
|
185
|
-
const fullPath = found.path ? `${key}.${found.path}` : key;
|
|
186
|
-
const candidate = { path: fullPath, items: found.items };
|
|
187
|
-
if (!best || candidate.items.length > best.items.length) best = candidate;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return best;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function inferCapabilityName(url: string): string {
|
|
194
|
-
const u = url.toLowerCase();
|
|
195
|
-
if (u.includes('hot') || u.includes('popular') || u.includes('ranking') || u.includes('trending')) return 'hot';
|
|
196
|
-
if (u.includes('search')) return 'search';
|
|
197
|
-
if (u.includes('feed') || u.includes('timeline') || u.includes('dynamic')) return 'feed';
|
|
198
|
-
if (u.includes('comment') || u.includes('reply')) return 'comments';
|
|
199
|
-
if (u.includes('history')) return 'history';
|
|
200
|
-
if (u.includes('profile') || u.includes('me')) return 'me';
|
|
201
|
-
if (u.includes('favorite') || u.includes('collect') || u.includes('bookmark')) return 'favorite';
|
|
202
|
-
try {
|
|
203
|
-
const segs = new URL(url).pathname
|
|
204
|
-
.split('/')
|
|
205
|
-
.filter(s => s && !s.match(/^\d+$/) && !s.match(/^[0-9a-f]{8,}$/i) && !s.match(/^v\d+$/));
|
|
206
|
-
if (segs.length) return segs[segs.length - 1].replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
207
|
-
} catch {}
|
|
208
|
-
return 'data';
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function inferStrategy(authIndicators: string[]): string {
|
|
212
|
-
if (authIndicators.includes('signature')) return 'intercept';
|
|
213
|
-
if (authIndicators.includes('bearer') || authIndicators.includes('csrf')) return 'header';
|
|
214
|
-
return 'cookie';
|
|
215
|
-
}
|
|
216
|
-
|
|
217
148
|
function scoreRequest(req: RecordedRequest, arrayResult: ReturnType<typeof findArrayPath> | null): number {
|
|
218
149
|
let s = 0;
|
|
219
150
|
if (arrayResult) {
|
|
@@ -267,10 +198,7 @@ function buildRecordedYaml(
|
|
|
267
198
|
: itemPath.split('.').map(p => `?.${p}`).join('');
|
|
268
199
|
|
|
269
200
|
// Detect search/limit/page params (must be before fetch URL building to use hasSearch/hasPage)
|
|
270
|
-
const
|
|
271
|
-
try { new URL(req.url).searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k)) qp.push(k); }); } catch {}
|
|
272
|
-
const hasSearch = qp.some(p => SEARCH_PARAMS.has(p));
|
|
273
|
-
const hasPage = qp.some(p => PAGINATION_PARAMS.has(p));
|
|
201
|
+
const { hasSearch, hasPagination: hasPage } = classifyQueryParams(req.url);
|
|
274
202
|
|
|
275
203
|
// Build evaluate script
|
|
276
204
|
const mapLines = Object.entries(detectedFields)
|
|
@@ -397,10 +325,9 @@ export async function recordSession(opts: RecordOptions): Promise<RecordResult>
|
|
|
397
325
|
const stop = () => { stopped = true; };
|
|
398
326
|
|
|
399
327
|
const { promise: enterPromise, cleanup: cleanupEnter } = waitForEnter();
|
|
400
|
-
|
|
328
|
+
enterPromise.then(stop);
|
|
401
329
|
const timeoutPromise = new Promise<void>(r => setTimeout(() => {
|
|
402
330
|
stop();
|
|
403
|
-
cleanupEnter(); // close readline to prevent process from hanging
|
|
404
331
|
r();
|
|
405
332
|
}, timeoutMs));
|
|
406
333
|
|
|
@@ -428,6 +355,7 @@ export async function recordSession(opts: RecordOptions): Promise<RecordResult>
|
|
|
428
355
|
}, pollMs);
|
|
429
356
|
|
|
430
357
|
await Promise.race([enterPromise, timeoutPromise]);
|
|
358
|
+
cleanupEnter(); // Always clean up readline to prevent process from hanging
|
|
431
359
|
clearInterval(pollInterval);
|
|
432
360
|
|
|
433
361
|
// Final drain from all known tabs
|
|
@@ -532,7 +460,7 @@ function analyzeAndWrite(
|
|
|
532
460
|
const scored: ScoredEntry[] = [];
|
|
533
461
|
for (const [pattern, req] of seen) {
|
|
534
462
|
const arrayResult = findArrayPath(req.body);
|
|
535
|
-
const authIndicators =
|
|
463
|
+
const authIndicators = detectAuthFromContent(req.url, req.body);
|
|
536
464
|
const score = scoreRequest(req, arrayResult);
|
|
537
465
|
if (score > 0) {
|
|
538
466
|
scored.push({ req, pattern, arrayResult, authIndicators, score });
|
package/src/registry-api.ts
CHANGED
|
@@ -10,3 +10,5 @@
|
|
|
10
10
|
export { cli, Strategy, getRegistry, fullName, registerCommand } from './registry.js';
|
|
11
11
|
export type { CliCommand, Arg, CliOptions } from './registry.js';
|
|
12
12
|
export type { IPage } from './types.js';
|
|
13
|
+
export { onStartup, onBeforeExecute, onAfterExecute } from './hooks.js';
|
|
14
|
+
export type { HookFn, HookContext, HookName } from './hooks.js';
|
package/src/registry.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface Arg {
|
|
|
22
22
|
choices?: string[];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface RequiredEnv {
|
|
26
|
+
name: string;
|
|
27
|
+
help?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- kwargs from CLI parsing are inherently untyped
|
|
26
31
|
export type CommandArgs = Record<string, any>;
|
|
27
32
|
|
|
@@ -40,6 +45,7 @@ export interface CliCommand {
|
|
|
40
45
|
/** Origin of this command: 'yaml', 'ts', or plugin name. */
|
|
41
46
|
source?: string;
|
|
42
47
|
footerExtra?: (kwargs: CommandArgs) => string | undefined;
|
|
48
|
+
requiredEnv?: RequiredEnv[];
|
|
43
49
|
/**
|
|
44
50
|
* Control pre-navigation for cookie/header context before command execution.
|
|
45
51
|
*
|
|
@@ -88,6 +94,7 @@ export function cli(opts: CliOptions): CliCommand {
|
|
|
88
94
|
pipeline: opts.pipeline,
|
|
89
95
|
timeoutSeconds: opts.timeoutSeconds,
|
|
90
96
|
footerExtra: opts.footerExtra,
|
|
97
|
+
requiredEnv: opts.requiredEnv,
|
|
91
98
|
navigateBefore: opts.navigateBefore,
|
|
92
99
|
};
|
|
93
100
|
|
|
@@ -112,7 +119,3 @@ export function registerCommand(cmd: CliCommand): void {
|
|
|
112
119
|
_registry.set(fullName(cmd), cmd);
|
|
113
120
|
}
|
|
114
121
|
|
|
115
|
-
// Re-export serialization helpers from their dedicated module
|
|
116
|
-
export { serializeArg, serializeCommand, formatArgSummary, formatRegistryHelpText } from './serialization.js';
|
|
117
|
-
export type { SerializedArg } from './serialization.js';
|
|
118
|
-
|
package/src/runtime.ts
CHANGED
|
@@ -10,10 +10,20 @@ export function getBrowserFactory(): new () => IBrowserFactory {
|
|
|
10
10
|
return (process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge) as unknown as new () => IBrowserFactory;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
function parseEnvTimeout(envVar: string, fallback: number): number {
|
|
14
|
+
const raw = process.env[envVar];
|
|
15
|
+
if (raw === undefined) return fallback;
|
|
16
|
+
const parsed = parseInt(raw, 10);
|
|
17
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
18
|
+
console.error(`[runtime] Invalid ${envVar}="${raw}", using default ${fallback}s`);
|
|
19
|
+
return fallback;
|
|
20
|
+
}
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_BROWSER_CONNECT_TIMEOUT = parseEnvTimeout('OPENCLI_BROWSER_CONNECT_TIMEOUT', 30);
|
|
25
|
+
export const DEFAULT_BROWSER_COMMAND_TIMEOUT = parseEnvTimeout('OPENCLI_BROWSER_COMMAND_TIMEOUT', 60);
|
|
26
|
+
export const DEFAULT_BROWSER_EXPLORE_TIMEOUT = parseEnvTimeout('OPENCLI_BROWSER_EXPLORE_TIMEOUT', 120);
|
|
17
27
|
|
|
18
28
|
/**
|
|
19
29
|
* Timeout with seconds unit. Used for high-level command timeouts.
|