@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
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImageX cover image uploader.
|
|
3
|
+
*
|
|
4
|
+
* Uploads a JPEG/PNG image to ByteDance ImageX via a pre-signed PUT URL
|
|
5
|
+
* obtained from the Douyin "apply cover upload" API.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { CommandExecutionError } from '../../../errors.js';
|
|
10
|
+
/**
|
|
11
|
+
* Detect MIME type from file extension.
|
|
12
|
+
* Falls back to image/jpeg for unknown extensions.
|
|
13
|
+
*/
|
|
14
|
+
function detectContentType(filePath) {
|
|
15
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
16
|
+
switch (ext) {
|
|
17
|
+
case '.png':
|
|
18
|
+
return 'image/png';
|
|
19
|
+
case '.gif':
|
|
20
|
+
return 'image/gif';
|
|
21
|
+
case '.webp':
|
|
22
|
+
return 'image/webp';
|
|
23
|
+
default:
|
|
24
|
+
return 'image/jpeg';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Upload a cover image to ByteDance ImageX via a pre-signed PUT URL.
|
|
29
|
+
*
|
|
30
|
+
* @param imagePath - Local file path to the image (JPEG/PNG/etc.)
|
|
31
|
+
* @param uploadInfo - Upload URL and store_uri from the apply cover upload API
|
|
32
|
+
* @returns The store_uri (= image_uri for use in create_v2)
|
|
33
|
+
*/
|
|
34
|
+
export async function imagexUpload(imagePath, uploadInfo) {
|
|
35
|
+
if (!fs.existsSync(imagePath)) {
|
|
36
|
+
throw new CommandExecutionError(`Cover image file not found: ${imagePath}`, 'Ensure the file path is correct and accessible.');
|
|
37
|
+
}
|
|
38
|
+
const imageBuffer = fs.readFileSync(imagePath);
|
|
39
|
+
const contentType = detectContentType(imagePath);
|
|
40
|
+
const res = await fetch(uploadInfo.upload_url, {
|
|
41
|
+
method: 'PUT',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': contentType,
|
|
44
|
+
'Content-Length': String(imageBuffer.byteLength),
|
|
45
|
+
},
|
|
46
|
+
body: imageBuffer,
|
|
47
|
+
});
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
const body = await res.text().catch(() => '');
|
|
50
|
+
throw new CommandExecutionError(`ImageX upload failed with status ${res.status}: ${body}`, 'Check that the upload URL is valid and has not expired.');
|
|
51
|
+
}
|
|
52
|
+
return uploadInfo.store_uri;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { CommandExecutionError } from '../../../errors.js';
|
|
6
|
+
import { imagexUpload } from './imagex-upload.js';
|
|
7
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
8
|
+
function makeTempImage(ext = '.jpg') {
|
|
9
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'imagex-test-'));
|
|
10
|
+
const filePath = path.join(dir, `cover${ext}`);
|
|
11
|
+
fs.writeFileSync(filePath, Buffer.from([0xff, 0xd8, 0xff, 0xe0])); // minimal JPEG header bytes
|
|
12
|
+
return filePath;
|
|
13
|
+
}
|
|
14
|
+
const FAKE_UPLOAD_INFO = {
|
|
15
|
+
upload_url: 'https://imagex.bytedance.com/upload/presigned/fake',
|
|
16
|
+
store_uri: 'tos-cn-i-alisg.example.com/cover/abc123',
|
|
17
|
+
};
|
|
18
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
19
|
+
describe('imagexUpload', () => {
|
|
20
|
+
let imagePath;
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
imagePath = makeTempImage('.jpg');
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
// Clean up temp files
|
|
26
|
+
try {
|
|
27
|
+
fs.unlinkSync(imagePath);
|
|
28
|
+
fs.rmdirSync(path.dirname(imagePath));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// ignore cleanup errors
|
|
32
|
+
}
|
|
33
|
+
vi.restoreAllMocks();
|
|
34
|
+
});
|
|
35
|
+
it('throws CommandExecutionError when image file does not exist', async () => {
|
|
36
|
+
await expect(imagexUpload('/nonexistent/path/cover.jpg', FAKE_UPLOAD_INFO)).rejects.toThrow(CommandExecutionError);
|
|
37
|
+
await expect(imagexUpload('/nonexistent/path/cover.jpg', FAKE_UPLOAD_INFO)).rejects.toThrow('Cover image file not found');
|
|
38
|
+
});
|
|
39
|
+
it('PUTs the image and returns store_uri on success', async () => {
|
|
40
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
41
|
+
ok: true,
|
|
42
|
+
status: 200,
|
|
43
|
+
text: vi.fn().mockResolvedValue(''),
|
|
44
|
+
});
|
|
45
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
46
|
+
const result = await imagexUpload(imagePath, FAKE_UPLOAD_INFO);
|
|
47
|
+
expect(result).toBe(FAKE_UPLOAD_INFO.store_uri);
|
|
48
|
+
expect(mockFetch).toHaveBeenCalledOnce();
|
|
49
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
50
|
+
expect(url).toBe(FAKE_UPLOAD_INFO.upload_url);
|
|
51
|
+
expect(init.method).toBe('PUT');
|
|
52
|
+
expect(init.headers['Content-Type']).toBe('image/jpeg');
|
|
53
|
+
});
|
|
54
|
+
it('uses image/png Content-Type for .png files', async () => {
|
|
55
|
+
const pngPath = makeTempImage('.png');
|
|
56
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
57
|
+
ok: true,
|
|
58
|
+
status: 200,
|
|
59
|
+
text: vi.fn().mockResolvedValue(''),
|
|
60
|
+
});
|
|
61
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
62
|
+
try {
|
|
63
|
+
await imagexUpload(pngPath, FAKE_UPLOAD_INFO);
|
|
64
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
65
|
+
expect(init.headers['Content-Type']).toBe('image/png');
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
try {
|
|
69
|
+
fs.unlinkSync(pngPath);
|
|
70
|
+
fs.rmdirSync(path.dirname(pngPath));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// ignore
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
it('throws CommandExecutionError on non-2xx PUT response', async () => {
|
|
78
|
+
const mockFetch = vi.fn().mockResolvedValue({
|
|
79
|
+
ok: false,
|
|
80
|
+
status: 403,
|
|
81
|
+
text: vi.fn().mockResolvedValue('Forbidden'),
|
|
82
|
+
});
|
|
83
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
84
|
+
await expect(imagexUpload(imagePath, FAKE_UPLOAD_INFO)).rejects.toThrow(CommandExecutionError);
|
|
85
|
+
await expect(imagexUpload(imagePath, FAKE_UPLOAD_INFO)).rejects.toThrow('ImageX upload failed with status 403');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IPage } from '../../../types.js';
|
|
2
|
+
import type { Sts2Credentials } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Fetch STS2 temporary credentials from the creator center.
|
|
5
|
+
* These are used to authenticate Node.js-side TOS multipart uploads.
|
|
6
|
+
* Returns: { access_key_id, secret_access_key, session_token, expired_time }
|
|
7
|
+
*/
|
|
8
|
+
export declare function getSts2Credentials(page: IPage): Promise<Sts2Credentials>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AuthRequiredError } from '../../../errors.js';
|
|
2
|
+
const STS2_URL = 'https://creator.douyin.com/aweme/mid/video/sts2/?scene=web&aid=1128&cookie_enabled=true&device_platform=web';
|
|
3
|
+
/**
|
|
4
|
+
* Fetch STS2 temporary credentials from the creator center.
|
|
5
|
+
* These are used to authenticate Node.js-side TOS multipart uploads.
|
|
6
|
+
* Returns: { access_key_id, secret_access_key, session_token, expired_time }
|
|
7
|
+
*/
|
|
8
|
+
export async function getSts2Credentials(page) {
|
|
9
|
+
const js = `fetch(${JSON.stringify(STS2_URL)}, { credentials: 'include' }).then(r => r.json())`;
|
|
10
|
+
const res = await page.evaluate(js);
|
|
11
|
+
if (!res?.data?.access_key_id) {
|
|
12
|
+
throw new AuthRequiredError('creator.douyin.com', 'STS2 credentials missing');
|
|
13
|
+
}
|
|
14
|
+
return res.data;
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface HashtagInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
id: number;
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
}
|
|
7
|
+
export interface TextExtraItem {
|
|
8
|
+
type: number;
|
|
9
|
+
hashtag_id: number;
|
|
10
|
+
hashtag_name: string;
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
caption_start: number;
|
|
14
|
+
caption_end: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function parseTextExtra(_text: string, hashtags: HashtagInfo[]): TextExtraItem[];
|
|
17
|
+
/** Extract hashtag names from text (e.g. "#话题" → ["话题"]) */
|
|
18
|
+
export declare function extractHashtagNames(text: string): string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function parseTextExtra(_text, hashtags) {
|
|
2
|
+
return hashtags.map((h) => ({
|
|
3
|
+
type: 1,
|
|
4
|
+
hashtag_id: h.id,
|
|
5
|
+
hashtag_name: h.name,
|
|
6
|
+
start: h.start,
|
|
7
|
+
end: h.end,
|
|
8
|
+
caption_start: 0,
|
|
9
|
+
caption_end: h.end - h.start,
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
/** Extract hashtag names from text (e.g. "#话题" → ["话题"]) */
|
|
13
|
+
export function extractHashtagNames(text) {
|
|
14
|
+
return [...text.matchAll(/#([^\s#]+)/g)].map((m) => m[1]);
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseTextExtra, extractHashtagNames } from './text-extra.js';
|
|
3
|
+
describe('parseTextExtra', () => {
|
|
4
|
+
it('returns empty array for text with no hashtags', () => {
|
|
5
|
+
const result = parseTextExtra('普通文本内容', []);
|
|
6
|
+
expect(result).toEqual([]);
|
|
7
|
+
});
|
|
8
|
+
it('produces type-1 entry for each hashtag', () => {
|
|
9
|
+
const hashtags = [
|
|
10
|
+
{ name: '话题', id: 12345, start: 5, end: 8 },
|
|
11
|
+
];
|
|
12
|
+
const result = parseTextExtra('普通文本 #话题', hashtags);
|
|
13
|
+
expect(result).toHaveLength(1);
|
|
14
|
+
expect(result[0]).toMatchObject({
|
|
15
|
+
type: 1,
|
|
16
|
+
hashtag_name: '话题',
|
|
17
|
+
hashtag_id: 12345,
|
|
18
|
+
start: 5,
|
|
19
|
+
end: 8,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
it('sets hashtag_id to 0 when not found', () => {
|
|
23
|
+
const hashtags = [
|
|
24
|
+
{ name: '未知话题', id: 0, start: 0, end: 5 },
|
|
25
|
+
];
|
|
26
|
+
const result = parseTextExtra('#未知话题', hashtags);
|
|
27
|
+
expect(result[0].hashtag_id).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('extractHashtagNames', () => {
|
|
31
|
+
it('extracts hashtag names from text', () => {
|
|
32
|
+
expect(extractHashtagNames('hello #foo and #bar')).toEqual(['foo', 'bar']);
|
|
33
|
+
});
|
|
34
|
+
it('returns empty array when no hashtags', () => {
|
|
35
|
+
expect(extractHashtagNames('no hashtags here')).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const MIN_OFFSET = 7200; // 2 hours
|
|
2
|
+
const MAX_OFFSET = 14 * 86400; // 14 days
|
|
3
|
+
export function validateTiming(unixSeconds) {
|
|
4
|
+
if (!Number.isFinite(unixSeconds))
|
|
5
|
+
throw new Error(`无效的时间戳: ${unixSeconds}`);
|
|
6
|
+
const now = Math.floor(Date.now() / 1000);
|
|
7
|
+
if (unixSeconds < now + MIN_OFFSET)
|
|
8
|
+
throw new Error(`定时发布时间必须在至少 2 小时后`);
|
|
9
|
+
if (unixSeconds > now + MAX_OFFSET)
|
|
10
|
+
throw new Error(`定时发布时间不能超过 14 天`);
|
|
11
|
+
}
|
|
12
|
+
export function toUnixSeconds(input) {
|
|
13
|
+
if (typeof input === 'number')
|
|
14
|
+
return input;
|
|
15
|
+
if (/^\d+$/.test(input)) {
|
|
16
|
+
return Number(input);
|
|
17
|
+
}
|
|
18
|
+
const ms = new Date(input).getTime();
|
|
19
|
+
if (isNaN(ms))
|
|
20
|
+
throw new Error(`无效的时间格式: "${input}"`);
|
|
21
|
+
return Math.floor(ms / 1000);
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { validateTiming, toUnixSeconds } from './timing.js';
|
|
3
|
+
describe('validateTiming', () => {
|
|
4
|
+
const now = () => Math.floor(Date.now() / 1000);
|
|
5
|
+
it('accepts a time 3 hours from now', () => {
|
|
6
|
+
expect(() => validateTiming(now() + 3 * 3600)).not.toThrow();
|
|
7
|
+
});
|
|
8
|
+
it('rejects a time less than 2 hours from now', () => {
|
|
9
|
+
expect(() => validateTiming(now() + 3600)).toThrow('至少 2 小时后');
|
|
10
|
+
});
|
|
11
|
+
it('rejects a time more than 14 days from now', () => {
|
|
12
|
+
expect(() => validateTiming(now() + 15 * 86400)).toThrow('不能超过 14 天');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe('toUnixSeconds', () => {
|
|
16
|
+
it('passes through a numeric unix timestamp', () => {
|
|
17
|
+
expect(toUnixSeconds(1744070400)).toBe(1744070400);
|
|
18
|
+
});
|
|
19
|
+
it('parses a numeric unix timestamp string', () => {
|
|
20
|
+
expect(toUnixSeconds('1744070400')).toBe(1744070400);
|
|
21
|
+
});
|
|
22
|
+
it('parses ISO8601 string', () => {
|
|
23
|
+
expect(toUnixSeconds('2026-04-08T12:00:00Z')).toBe(Math.floor(new Date('2026-04-08T12:00:00Z').getTime() / 1000));
|
|
24
|
+
});
|
|
25
|
+
it('throws on invalid input', () => {
|
|
26
|
+
expect(() => toUnixSeconds('not-a-date')).toThrow();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the fs.readSync short-read guard in tosUpload.
|
|
3
|
+
*
|
|
4
|
+
* This file is separate from tos-upload.test.ts because vi.mock is hoisted and
|
|
5
|
+
* would interfere with the real-fs tests there.
|
|
6
|
+
*
|
|
7
|
+
* Strategy:
|
|
8
|
+
* - Use setReadSyncOverride (exported testing seam) to force readSync to return 0
|
|
9
|
+
* - Mock global fetch to satisfy initMultipartUpload so the code path reaches readSync
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the fs.readSync short-read guard in tosUpload.
|
|
3
|
+
*
|
|
4
|
+
* This file is separate from tos-upload.test.ts because vi.mock is hoisted and
|
|
5
|
+
* would interfere with the real-fs tests there.
|
|
6
|
+
*
|
|
7
|
+
* Strategy:
|
|
8
|
+
* - Use setReadSyncOverride (exported testing seam) to force readSync to return 0
|
|
9
|
+
* - Mock global fetch to satisfy initMultipartUpload so the code path reaches readSync
|
|
10
|
+
*/
|
|
11
|
+
import * as actualFs from 'node:fs';
|
|
12
|
+
import * as os from 'node:os';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
15
|
+
import { CommandExecutionError } from '../../../errors.js';
|
|
16
|
+
import { setReadSyncOverride, tosUpload } from './tos-upload.js';
|
|
17
|
+
/** Build a minimal fetch mock that satisfies initMultipartUpload (POST ?uploads → 200 + UploadId XML). */
|
|
18
|
+
function makeFetchMock() {
|
|
19
|
+
return vi.fn().mockResolvedValue({
|
|
20
|
+
status: 200,
|
|
21
|
+
text: async () => '<InitiateMultipartUploadResult><UploadId>mock-upload-id</UploadId></InitiateMultipartUploadResult>',
|
|
22
|
+
headers: { forEach: (_cb) => { } },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
describe('tosUpload short-read guard', () => {
|
|
26
|
+
let tmpDir;
|
|
27
|
+
let tmpFile;
|
|
28
|
+
let originalFetch;
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
originalFetch = globalThis.fetch;
|
|
31
|
+
tmpDir = actualFs.mkdtempSync(path.join(os.tmpdir(), 'tos-upload-shortread-'));
|
|
32
|
+
tmpFile = path.join(tmpDir, 'video.mp4');
|
|
33
|
+
// 100-byte file — fits in a single part
|
|
34
|
+
actualFs.writeFileSync(tmpFile, Buffer.alloc(100, 0xff));
|
|
35
|
+
});
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
setReadSyncOverride(null);
|
|
38
|
+
globalThis.fetch = originalFetch;
|
|
39
|
+
actualFs.rmSync(tmpDir, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
it('throws CommandExecutionError on short read', async () => {
|
|
42
|
+
// Mock fetch so initMultipartUpload succeeds and code reaches readSync
|
|
43
|
+
globalThis.fetch = makeFetchMock();
|
|
44
|
+
// Override readSync to return 0 (fewer bytes than requested)
|
|
45
|
+
setReadSyncOverride(() => 0);
|
|
46
|
+
const mockCredentials = {
|
|
47
|
+
access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
|
48
|
+
secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
49
|
+
session_token: 'test-session-token',
|
|
50
|
+
expired_time: Date.now() / 1000 + 3600,
|
|
51
|
+
};
|
|
52
|
+
const uploadInfo = {
|
|
53
|
+
tos_upload_url: 'https://tos-cn-i-alisg.volces.com/bucket/key',
|
|
54
|
+
auth: 'AWS4-HMAC-SHA256 Credential=test',
|
|
55
|
+
video_id: 'test-video-id',
|
|
56
|
+
};
|
|
57
|
+
await expect(tosUpload({
|
|
58
|
+
filePath: tmpFile,
|
|
59
|
+
uploadInfo,
|
|
60
|
+
credentials: mockCredentials,
|
|
61
|
+
})).rejects.toThrow(CommandExecutionError);
|
|
62
|
+
});
|
|
63
|
+
it('error message identifies the part number and byte counts', async () => {
|
|
64
|
+
globalThis.fetch = makeFetchMock();
|
|
65
|
+
setReadSyncOverride(() => 0);
|
|
66
|
+
const mockCredentials = {
|
|
67
|
+
access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
|
68
|
+
secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
69
|
+
session_token: 'test-session-token',
|
|
70
|
+
expired_time: Date.now() / 1000 + 3600,
|
|
71
|
+
};
|
|
72
|
+
const uploadInfo = {
|
|
73
|
+
tos_upload_url: 'https://tos-cn-i-alisg.volces.com/bucket/key',
|
|
74
|
+
auth: 'AWS4-HMAC-SHA256 Credential=test',
|
|
75
|
+
video_id: 'test-video-id',
|
|
76
|
+
};
|
|
77
|
+
await expect(tosUpload({
|
|
78
|
+
filePath: tmpFile,
|
|
79
|
+
uploadInfo,
|
|
80
|
+
credentials: mockCredentials,
|
|
81
|
+
})).rejects.toThrow(/Short read on part 1: expected 100 bytes, got 0/);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOS (ByteDance Object Storage) multipart uploader with resume support.
|
|
3
|
+
*
|
|
4
|
+
* Uses AWS Signature V4 (HMAC-SHA256) with STS2 temporary credentials.
|
|
5
|
+
* For the init multipart upload call, the pre-computed auth from TosUploadInfo is used.
|
|
6
|
+
* For PUT part uploads and the final complete call, AWS4 is computed from STS2 credentials.
|
|
7
|
+
*/
|
|
8
|
+
import type { Sts2Credentials, TosUploadInfo } from './types.js';
|
|
9
|
+
export interface TosUploadOptions {
|
|
10
|
+
filePath: string;
|
|
11
|
+
uploadInfo: TosUploadInfo;
|
|
12
|
+
credentials: Sts2Credentials;
|
|
13
|
+
onProgress?: (uploaded: number, total: number) => void;
|
|
14
|
+
}
|
|
15
|
+
interface ResumePart {
|
|
16
|
+
partNumber: number;
|
|
17
|
+
etag: string;
|
|
18
|
+
}
|
|
19
|
+
interface ResumeState {
|
|
20
|
+
uploadId: string;
|
|
21
|
+
fileSize: number;
|
|
22
|
+
parts: ResumePart[];
|
|
23
|
+
}
|
|
24
|
+
declare const PART_SIZE: number;
|
|
25
|
+
declare const RESUME_DIR: string;
|
|
26
|
+
declare function getResumeFilePath(filePath: string): string;
|
|
27
|
+
declare function loadResumeState(resumePath: string, fileSize: number): ResumeState | null;
|
|
28
|
+
declare function saveResumeState(resumePath: string, state: ResumeState): void;
|
|
29
|
+
declare function deleteResumeState(resumePath: string): void;
|
|
30
|
+
declare function extractRegionFromHost(host: string): string;
|
|
31
|
+
interface SignedHeaders {
|
|
32
|
+
[key: string]: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Compute AWS Signature V4 headers for a TOS request.
|
|
36
|
+
* Returns a Record of all headers to include (including Authorization, x-amz-date, etc.)
|
|
37
|
+
*/
|
|
38
|
+
declare function computeAws4Headers(opts: {
|
|
39
|
+
method: string;
|
|
40
|
+
url: string;
|
|
41
|
+
headers: Record<string, string>;
|
|
42
|
+
body: Buffer | string;
|
|
43
|
+
credentials: Sts2Credentials;
|
|
44
|
+
service: string;
|
|
45
|
+
region: string;
|
|
46
|
+
datetime: string;
|
|
47
|
+
}): SignedHeaders;
|
|
48
|
+
type ReadSyncFn = (fd: number, buffer: Buffer, offset: number, length: number, position: number) => number;
|
|
49
|
+
/** @internal — for testing only */
|
|
50
|
+
export declare function setReadSyncOverride(fn: ReadSyncFn | null): void;
|
|
51
|
+
export declare function tosUpload(options: TosUploadOptions): Promise<void>;
|
|
52
|
+
export { PART_SIZE, RESUME_DIR, extractRegionFromHost, getResumeFilePath, loadResumeState, saveResumeState, deleteResumeState, computeAws4Headers, };
|
|
53
|
+
export type { ResumeState, ResumePart };
|