@agentrhq/webcmd 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -218
- package/cli-manifest.json +16247 -34913
- package/clis/_atlassian/shared.js +1 -1
- package/clis/booking/search.js +4 -4
- package/clis/chatgpt/ask.js +2 -2
- package/clis/chatgpt/utils.js +41 -42
- package/clis/chatgpt/utils.test.js +23 -23
- package/clis/chatgpt-app/ax.js +5 -5
- package/clis/chatgpt-app/ax.test.js +8 -8
- package/clis/chatgpt-app/new.js +2 -2
- package/clis/codex/sidebar.js +1 -1
- package/clis/codex/sidebar.test.js +12 -12
- package/clis/coingecko/top.js +3 -3
- package/clis/dblp/dblp.test.js +1 -1
- package/clis/discord-app/search.js +1 -1
- package/clis/discord-app/utils.js +6 -6
- package/clis/facebook/__fixtures__/notifications-page.html +6 -6
- package/clis/facebook/add-friend.js +2 -2
- package/clis/facebook/feed.js +18 -18
- package/clis/facebook/friends.js +4 -4
- package/clis/facebook/groups.js +3 -3
- package/clis/facebook/join-group.js +2 -2
- package/clis/facebook/marketplace-inbox.js +3 -3
- package/clis/facebook/marketplace-listings.js +1 -1
- package/clis/facebook/notifications.js +14 -15
- package/clis/facebook/notifications.test.js +28 -29
- package/clis/gemini/ask.test.js +12 -12
- package/clis/gemini/deep-research-result.js +2 -2
- package/clis/gemini/deep-research-result.test.js +2 -2
- package/clis/gemini/deep-research.js +2 -2
- package/clis/gemini/deep-research.test.js +1 -1
- package/clis/gemini/models.js +11 -11
- package/clis/gemini/models.test.js +1 -1
- package/clis/gemini/reply-state.test.js +89 -89
- package/clis/gemini/utils.js +41 -41
- package/clis/gemini/utils.test.js +30 -30
- package/clis/geogebra/utils.js +1 -1
- package/clis/google-scholar/search.js +3 -3
- package/clis/grok/delete.js +1 -1
- package/clis/grok/export-all.js +2 -2
- package/clis/grok/export.js +2 -2
- package/clis/grok/pin.js +3 -3
- package/clis/grok/utils.js +12 -12
- package/clis/grok/utils.test.js +2 -2
- package/clis/indeed/indeed.test.js +3 -3
- package/clis/indeed/utils.js +1 -1
- package/clis/instagram/post.js +19 -19
- package/clis/instagram/post.test.js +2 -2
- package/clis/instagram/reel.js +6 -6
- package/clis/linkedin/connect.js +3 -3
- package/clis/linkedin/people-search.js +4 -4
- package/clis/linkedin/profile-experience.js +6 -6
- package/clis/linkedin/profile-experience.test.js +10 -10
- package/clis/linkedin/profile-read.js +1 -1
- package/clis/linkedin/safe-send.js +1 -1
- package/clis/linkedin/search.js +1 -1
- package/clis/linkedin/shared.js +2 -2
- package/clis/linkedin/timeline.js +5 -5
- package/clis/manus/manus.test.js +2 -2
- package/clis/medium/feed.js +3 -3
- package/clis/medium/search.js +3 -3
- package/clis/medium/user.js +3 -3
- package/clis/notebooklm/auth.js +2 -2
- package/clis/notebooklm/note-list.test.js +4 -4
- package/clis/notebooklm/notes-get.test.js +8 -8
- package/clis/notebooklm/source-fulltext.test.js +11 -11
- package/clis/notebooklm/utils.js +28 -28
- package/clis/notebooklm/utils.test.js +10 -10
- package/clis/pixiv/search.test.js +3 -3
- package/clis/qoder/quest.js +2 -2
- package/clis/reddit/home.js +2 -2
- package/clis/reddit/hot.js +1 -1
- package/clis/reddit/read.js +2 -2
- package/clis/reddit/read.test.js +1 -1
- package/clis/reddit/reply.js +2 -2
- package/clis/reddit/whoami.js +2 -2
- package/clis/reuters/article-detail.js +1 -1
- package/clis/reuters/search.js +1 -1
- package/clis/slock/attachment-upload.test.js +1 -1
- package/clis/slock/task-convert.js +1 -1
- package/clis/substack/feed.js +3 -3
- package/clis/substack/publication.js +3 -3
- package/clis/substack/search.js +4 -4
- package/clis/tiktok/comment.js +2 -2
- package/clis/tiktok/follow.js +4 -4
- package/clis/tiktok/like.js +1 -1
- package/clis/tiktok/save.js +1 -1
- package/clis/tiktok/unfollow.js +4 -4
- package/clis/tiktok/unlike.js +1 -1
- package/clis/tiktok/unsave.js +2 -2
- package/clis/tiktok/write-refactor.test.js +5 -5
- package/clis/twitter/accept.js +3 -3
- package/clis/twitter/followers.js +1 -1
- package/clis/twitter/list-remove-core.js +2 -2
- package/clis/twitter/list-tweets.test.js +3 -3
- package/clis/twitter/lists.js +6 -6
- package/clis/twitter/lists.test.js +2 -2
- package/clis/twitter/reply-dm.js +1 -1
- package/clis/twitter/shared.test.js +4 -5
- package/clis/twitter/trending.js +1 -1
- package/clis/twitter/utils.js +1 -1
- package/clis/web/read.js +1 -1
- package/clis/web/read.test.js +2 -2
- package/clis/yahoo-finance/quote.js +1 -1
- package/clis/youtube/history.js +4 -4
- package/clis/youtube/transcript-group.js +1 -1
- package/clis/youtube/transcript-group.test.js +5 -5
- package/clis/youtube/transcript.js +5 -5
- package/clis/youtube/video.js +4 -4
- package/clis/youtube/video.test.js +6 -6
- package/dist/src/browser/analyze.test.js +13 -13
- package/dist/src/browser/bridge-readiness.d.ts +1 -1
- package/dist/src/browser/daemon-client.d.ts +1 -0
- package/dist/src/browser/daemon-client.js +102 -50
- package/dist/src/browser/daemon-client.test.js +104 -19
- package/dist/src/browser/errors.js +11 -0
- package/dist/src/browser/protocol.d.ts +2 -0
- package/dist/src/browser.test.js +2 -2
- package/dist/src/cli-argv-preprocess.d.ts +2 -2
- package/dist/src/cli-argv-preprocess.js +2 -2
- package/dist/src/cli-argv-preprocess.test.js +35 -35
- package/dist/src/cli.js +2 -2
- package/dist/src/cli.test.js +57 -57
- package/dist/src/commanderAdapter.test.js +19 -19
- package/dist/src/convention-audit.js +1 -1
- package/dist/src/daemon/server.js +28 -23
- package/dist/src/daemon/server.test.js +52 -0
- package/dist/src/daemon-utils.d.ts +1 -0
- package/dist/src/daemon-utils.js +9 -0
- package/dist/src/daemon.test.js +10 -1
- package/dist/src/doctor.js +7 -1
- package/dist/src/doctor.test.js +8 -1
- package/dist/src/download/article-download.d.ts +3 -3
- package/dist/src/download/article-download.js +7 -7
- package/dist/src/download/article-download.test.js +6 -6
- package/dist/src/download/index.js +1 -1
- package/dist/src/download/index.test.js +1 -2
- package/dist/src/download/media-download.d.ts +1 -1
- package/dist/src/download/media-download.js +1 -1
- package/dist/src/electron-apps.js +0 -8
- package/dist/src/electron-apps.test.js +2 -11
- package/dist/src/errors.test.js +9 -9
- package/dist/src/execution.js +2 -0
- package/dist/src/execution.test.js +12 -0
- package/dist/src/external-clis.yaml +0 -37
- package/dist/src/external.d.ts +1 -1
- package/dist/src/external.test.js +2 -17
- package/dist/src/generate-release-notes-cli.test.js +140 -0
- package/dist/src/help.d.ts +3 -3
- package/dist/src/help.test.js +8 -8
- package/dist/src/main.js +1 -1
- package/dist/src/manifest-types.d.ts +1 -1
- package/dist/src/node-network.test.js +2 -2
- package/dist/src/output.js +1 -1
- package/dist/src/pipeline/steps/download.test.js +3 -3
- package/dist/src/registry.test.js +2 -2
- package/dist/src/release-notes.d.ts +38 -0
- package/dist/src/release-notes.js +99 -0
- package/dist/src/release-notes.test.js +61 -0
- package/dist/src/serialization.test.js +7 -7
- package/dist/src/snapshotFormatter.js +3 -5
- package/dist/src/snapshotFormatter.test.d.ts +1 -1
- package/dist/src/snapshotFormatter.test.js +50 -50
- package/dist/src/tui.js +1 -1
- package/package.json +4 -1
- package/scripts/generate-release-notes.ts +234 -0
- package/scripts/postinstall.js +1 -1
- package/scripts/silent-column-drop-baseline.json +0 -498
- package/scripts/typed-error-lint-baseline.json +0 -464
- package/skills/webcmd-adapter-author/SKILL.md +216 -213
- package/skills/webcmd-adapter-author/references/adapter-template.md +206 -399
- package/skills/webcmd-adapter-author/references/api-discovery.md +111 -220
- package/skills/webcmd-adapter-author/references/coverage-matrix.md +34 -68
- package/skills/webcmd-adapter-author/references/field-conventions.md +84 -167
- package/skills/webcmd-adapter-author/references/field-decode-playbook.md +60 -146
- package/skills/webcmd-adapter-author/references/jsdom-fixture-pattern.md +111 -178
- package/skills/webcmd-adapter-author/references/output-design.md +47 -125
- package/skills/webcmd-adapter-author/references/site-memory.md +116 -166
- package/skills/webcmd-adapter-author/references/site-recon.md +74 -78
- package/skills/webcmd-adapter-author/references/strategy-selection.md +80 -124
- package/skills/webcmd-adapter-author/references/success-rate-pitfalls.md +80 -105
- package/skills/webcmd-adapter-author/references/typed-errors.md +87 -178
- package/skills/webcmd-autofix/SKILL.md +136 -143
- package/skills/webcmd-sitemap-author/SKILL.md +58 -67
- package/skills/webcmd-sitemap-author/references/sitemap-schema.md +354 -494
- package/skills/webcmd-usage/SKILL.md +106 -97
- package/clis/12306/auth.js +0 -59
- package/clis/12306/me.js +0 -73
- package/clis/12306/orders.js +0 -96
- package/clis/12306/passengers.js +0 -90
- package/clis/12306/price.js +0 -166
- package/clis/12306/stations.js +0 -66
- package/clis/12306/train.js +0 -91
- package/clis/12306/trains.js +0 -166
- package/clis/12306/utils.js +0 -272
- package/clis/12306/utils.test.js +0 -424
- package/clis/1688/assets.js +0 -205
- package/clis/1688/assets.test.js +0 -39
- package/clis/1688/auth.js +0 -46
- package/clis/1688/download.js +0 -77
- package/clis/1688/download.test.js +0 -31
- package/clis/1688/item.js +0 -188
- package/clis/1688/item.test.js +0 -67
- package/clis/1688/search.js +0 -310
- package/clis/1688/search.test.js +0 -75
- package/clis/1688/shared.js +0 -557
- package/clis/1688/shared.test.js +0 -57
- package/clis/1688/store.js +0 -227
- package/clis/1688/store.test.js +0 -62
- package/clis/1point3acres/auth.js +0 -52
- package/clis/1point3acres/digest.js +0 -35
- package/clis/1point3acres/forum.js +0 -51
- package/clis/1point3acres/forums.js +0 -44
- package/clis/1point3acres/hot.js +0 -35
- package/clis/1point3acres/latest.js +0 -35
- package/clis/1point3acres/notifications.js +0 -64
- package/clis/1point3acres/search.js +0 -71
- package/clis/1point3acres/thread.js +0 -117
- package/clis/1point3acres/user.js +0 -77
- package/clis/1point3acres/utils.js +0 -247
- package/clis/36kr/article.js +0 -66
- package/clis/36kr/article.test.js +0 -46
- package/clis/36kr/hot.js +0 -87
- package/clis/36kr/hot.test.js +0 -15
- package/clis/36kr/news.js +0 -52
- package/clis/36kr/news.test.js +0 -85
- package/clis/36kr/search.js +0 -79
- package/clis/51job/company.js +0 -126
- package/clis/51job/detail.js +0 -109
- package/clis/51job/hot.js +0 -56
- package/clis/51job/search.js +0 -80
- package/clis/51job/utils.js +0 -302
- package/clis/51job/utils.test.js +0 -69
- package/clis/aibase/news.js +0 -110
- package/clis/aibase/news.test.js +0 -59
- package/clis/autohome/__fixtures__/catalog.html +0 -11
- package/clis/autohome/__fixtures__/koubei.json +0 -116
- package/clis/autohome/autohome.test.js +0 -115
- package/clis/autohome/brand.js +0 -108
- package/clis/autohome/score.js +0 -103
- package/clis/autohome/utils.js +0 -157
- package/clis/baidu-scholar/search.js +0 -87
- package/clis/baidu-scholar/search.test.js +0 -23
- package/clis/bilibili/auth.js +0 -36
- package/clis/bilibili/comment.js +0 -107
- package/clis/bilibili/comment.test.js +0 -153
- package/clis/bilibili/comments.js +0 -136
- package/clis/bilibili/comments.test.js +0 -142
- package/clis/bilibili/download.js +0 -178
- package/clis/bilibili/download.test.js +0 -173
- package/clis/bilibili/dynamic.js +0 -34
- package/clis/bilibili/dynamic.test.js +0 -68
- package/clis/bilibili/favorite.js +0 -47
- package/clis/bilibili/feed.js +0 -220
- package/clis/bilibili/follow.js +0 -140
- package/clis/bilibili/follow.test.js +0 -203
- package/clis/bilibili/following.js +0 -44
- package/clis/bilibili/history.js +0 -45
- package/clis/bilibili/hot.js +0 -40
- package/clis/bilibili/hot.test.js +0 -17
- package/clis/bilibili/me.js +0 -13
- package/clis/bilibili/ranking.js +0 -25
- package/clis/bilibili/search.js +0 -24
- package/clis/bilibili/subtitle.js +0 -141
- package/clis/bilibili/subtitle.test.js +0 -240
- package/clis/bilibili/summary.js +0 -167
- package/clis/bilibili/summary.test.js +0 -210
- package/clis/bilibili/unfollow.js +0 -121
- package/clis/bilibili/user-videos.js +0 -39
- package/clis/bilibili/utils.js +0 -273
- package/clis/bilibili/utils.test.js +0 -97
- package/clis/bilibili/video.js +0 -155
- package/clis/bilibili/video.test.js +0 -308
- package/clis/boss/auth.js +0 -47
- package/clis/boss/batchgreet.js +0 -61
- package/clis/boss/chatlist.js +0 -116
- package/clis/boss/chatlist.test.js +0 -211
- package/clis/boss/chatmsg.js +0 -117
- package/clis/boss/chatmsg.test.js +0 -230
- package/clis/boss/detail.js +0 -63
- package/clis/boss/exchange.js +0 -46
- package/clis/boss/greet.js +0 -49
- package/clis/boss/invite.js +0 -64
- package/clis/boss/joblist.js +0 -31
- package/clis/boss/mark.js +0 -70
- package/clis/boss/recommend.js +0 -45
- package/clis/boss/resume.js +0 -150
- package/clis/boss/search.js +0 -197
- package/clis/boss/search.test.js +0 -78
- package/clis/boss/send.js +0 -44
- package/clis/boss/stats.js +0 -66
- package/clis/boss/utils.js +0 -468
- package/clis/boss/utils.test.js +0 -34
- package/clis/chaoxing/assignments.js +0 -76
- package/clis/chaoxing/auth.js +0 -54
- package/clis/chaoxing/exams.js +0 -75
- package/clis/chaoxing/utils.js +0 -223
- package/clis/chaoxing/utils.test.js +0 -45
- package/clis/cnki/search.js +0 -61
- package/clis/cnki/search.test.js +0 -18
- package/clis/ctrip/auth.js +0 -50
- package/clis/ctrip/ctrip.test.js +0 -719
- package/clis/ctrip/flight.js +0 -136
- package/clis/ctrip/hotel-search.js +0 -132
- package/clis/ctrip/hotel-suggest.js +0 -45
- package/clis/ctrip/search.js +0 -41
- package/clis/ctrip/utils.js +0 -473
- package/clis/deepseek/ask.js +0 -158
- package/clis/deepseek/ask.test.js +0 -399
- package/clis/deepseek/auth.js +0 -54
- package/clis/deepseek/detail.js +0 -46
- package/clis/deepseek/detail.test.js +0 -81
- package/clis/deepseek/history.js +0 -27
- package/clis/deepseek/new.js +0 -33
- package/clis/deepseek/read.js +0 -25
- package/clis/deepseek/send.js +0 -140
- package/clis/deepseek/send.test.js +0 -107
- package/clis/deepseek/status.js +0 -26
- package/clis/deepseek/utils.js +0 -487
- package/clis/deepseek/utils.test.js +0 -370
- package/clis/dianping/__fixtures__/search.html +0 -168
- package/clis/dianping/__fixtures__/shop.html +0 -6
- package/clis/dianping/auth.js +0 -49
- package/clis/dianping/cityResolver.js +0 -185
- package/clis/dianping/dianping.test.js +0 -578
- package/clis/dianping/search.js +0 -157
- package/clis/dianping/shop.js +0 -173
- package/clis/dianping/utils.js +0 -157
- package/clis/dongchedi/__fixtures__/search.json +0 -60
- package/clis/dongchedi/__fixtures__/series-detail.json +0 -911
- package/clis/dongchedi/__fixtures__/series-score.json +0 -1396
- package/clis/dongchedi/dongchedi.test.js +0 -191
- package/clis/dongchedi/koubei.js +0 -85
- package/clis/dongchedi/models.js +0 -91
- package/clis/dongchedi/score.js +0 -83
- package/clis/dongchedi/search.js +0 -83
- package/clis/dongchedi/series.js +0 -87
- package/clis/dongchedi/specs.js +0 -115
- package/clis/dongchedi/utils.js +0 -180
- package/clis/douban/auth.js +0 -50
- package/clis/douban/book-hot.js +0 -15
- package/clis/douban/download.js +0 -68
- package/clis/douban/download.test.js +0 -170
- package/clis/douban/marks.js +0 -116
- package/clis/douban/movie-hot.js +0 -15
- package/clis/douban/movie-hot.test.js +0 -14
- package/clis/douban/photos.js +0 -35
- package/clis/douban/reviews.js +0 -107
- package/clis/douban/search.js +0 -18
- package/clis/douban/search.test.js +0 -11
- package/clis/douban/subject.js +0 -46
- package/clis/douban/subject.test.js +0 -11
- package/clis/douban/top250.js +0 -68
- package/clis/douban/utils.js +0 -657
- package/clis/douban/utils.test.js +0 -488
- package/clis/doubao/ask.js +0 -40
- package/clis/doubao/auth.js +0 -66
- package/clis/doubao/detail.js +0 -35
- package/clis/doubao/detail.test.js +0 -42
- package/clis/doubao/history.js +0 -30
- package/clis/doubao/history.test.js +0 -37
- package/clis/doubao/meeting-summary.js +0 -41
- package/clis/doubao/meeting-transcript.js +0 -38
- package/clis/doubao/new.js +0 -22
- package/clis/doubao/read.js +0 -21
- package/clis/doubao/send.js +0 -24
- package/clis/doubao/status.js +0 -26
- package/clis/doubao/utils.js +0 -1156
- package/clis/doubao/utils.test.js +0 -396
- package/clis/doubao-app/ask.js +0 -54
- package/clis/doubao-app/dump.js +0 -25
- package/clis/doubao-app/new.js +0 -21
- package/clis/doubao-app/read.js +0 -19
- package/clis/doubao-app/screenshot.js +0 -19
- package/clis/doubao-app/send.js +0 -28
- package/clis/doubao-app/status.js +0 -17
- package/clis/doubao-app/utils.js +0 -110
- package/clis/douyin/_shared/browser-fetch.js +0 -66
- package/clis/douyin/_shared/browser-fetch.test.js +0 -64
- package/clis/douyin/_shared/creation-id.js +0 -5
- package/clis/douyin/_shared/creation-id.test.js +0 -22
- package/clis/douyin/_shared/evaluate-result.js +0 -16
- package/clis/douyin/_shared/imagex-upload.js +0 -53
- package/clis/douyin/_shared/imagex-upload.test.js +0 -87
- package/clis/douyin/_shared/public-api.js +0 -29
- package/clis/douyin/_shared/sts2.js +0 -21
- package/clis/douyin/_shared/sts2.test.js +0 -27
- package/clis/douyin/_shared/text-extra.js +0 -15
- package/clis/douyin/_shared/text-extra.test.js +0 -37
- package/clis/douyin/_shared/timing.js +0 -22
- package/clis/douyin/_shared/timing.test.js +0 -28
- package/clis/douyin/_shared/tos-upload-short-read.test.js +0 -83
- package/clis/douyin/_shared/tos-upload.js +0 -331
- package/clis/douyin/_shared/tos-upload.test.js +0 -229
- package/clis/douyin/_shared/transcode.js +0 -45
- package/clis/douyin/_shared/transcode.test.js +0 -91
- package/clis/douyin/_shared/vod-upload.js +0 -212
- package/clis/douyin/_shared/vod-upload.test.js +0 -38
- package/clis/douyin/activities.js +0 -23
- package/clis/douyin/activities.test.js +0 -55
- package/clis/douyin/auth.js +0 -39
- package/clis/douyin/collections.js +0 -23
- package/clis/douyin/collections.test.js +0 -45
- package/clis/douyin/delete.js +0 -152
- package/clis/douyin/delete.test.js +0 -100
- package/clis/douyin/draft.js +0 -355
- package/clis/douyin/draft.test.js +0 -335
- package/clis/douyin/drafts.js +0 -24
- package/clis/douyin/drafts.test.js +0 -11
- package/clis/douyin/hashtag.js +0 -114
- package/clis/douyin/hashtag.test.js +0 -171
- package/clis/douyin/location.js +0 -25
- package/clis/douyin/location.test.js +0 -23
- package/clis/douyin/profile.js +0 -29
- package/clis/douyin/profile.test.js +0 -46
- package/clis/douyin/publish-upload-id.test.js +0 -206
- package/clis/douyin/publish.js +0 -341
- package/clis/douyin/publish.test.js +0 -38
- package/clis/douyin/search.js +0 -308
- package/clis/douyin/search.test.js +0 -307
- package/clis/douyin/stats.js +0 -28
- package/clis/douyin/stats.test.js +0 -22
- package/clis/douyin/update.js +0 -32
- package/clis/douyin/update.test.js +0 -11
- package/clis/douyin/user-videos.js +0 -82
- package/clis/douyin/user-videos.test.js +0 -151
- package/clis/douyin/videos.js +0 -52
- package/clis/douyin/videos.test.js +0 -54
- package/clis/eastmoney/_secid.js +0 -78
- package/clis/eastmoney/announcement.js +0 -53
- package/clis/eastmoney/convertible.js +0 -74
- package/clis/eastmoney/etf.js +0 -66
- package/clis/eastmoney/holders.js +0 -79
- package/clis/eastmoney/hot-rank.js +0 -51
- package/clis/eastmoney/hot-rank.test.js +0 -59
- package/clis/eastmoney/index-board.js +0 -97
- package/clis/eastmoney/kline.js +0 -88
- package/clis/eastmoney/kuaixun.js +0 -55
- package/clis/eastmoney/longhu.js +0 -68
- package/clis/eastmoney/money-flow.js +0 -79
- package/clis/eastmoney/northbound.js +0 -58
- package/clis/eastmoney/quote.js +0 -108
- package/clis/eastmoney/rank.js +0 -95
- package/clis/eastmoney/sectors.js +0 -77
- package/clis/flomo/auth.js +0 -55
- package/clis/flomo/memos.js +0 -228
- package/clis/flomo/memos.test.js +0 -144
- package/clis/gitee/auth.js +0 -41
- package/clis/gitee/index.js +0 -3
- package/clis/gitee/search.js +0 -137
- package/clis/gitee/search.test.js +0 -65
- package/clis/gitee/trending.js +0 -568
- package/clis/gitee/user.js +0 -200
- package/clis/gitee/user.test.js +0 -63
- package/clis/gov-law/commands.test.js +0 -39
- package/clis/gov-law/recent.js +0 -22
- package/clis/gov-law/search.js +0 -41
- package/clis/gov-law/shared.js +0 -51
- package/clis/gov-policy/__fixtures__/recent.html +0 -16
- package/clis/gov-policy/__fixtures__/search.html +0 -41
- package/clis/gov-policy/gov-policy.test.js +0 -224
- package/clis/gov-policy/recent.js +0 -90
- package/clis/gov-policy/search.js +0 -91
- package/clis/gov-policy/utils.js +0 -54
- package/clis/guazi/__fixtures__/detail.html +0 -7
- package/clis/guazi/__fixtures__/list.html +0 -5
- package/clis/guazi/browse.js +0 -95
- package/clis/guazi/car.js +0 -110
- package/clis/guazi/guazi.test.js +0 -126
- package/clis/guazi/utils.js +0 -133
- package/clis/huodongxing/events.js +0 -322
- package/clis/huodongxing/events.test.js +0 -296
- package/clis/hupu/__fixtures__/hot-home.html +0 -64
- package/clis/hupu/auth.js +0 -47
- package/clis/hupu/detail.js +0 -72
- package/clis/hupu/hot.js +0 -163
- package/clis/hupu/hot.test.js +0 -224
- package/clis/hupu/like.js +0 -76
- package/clis/hupu/mentions.js +0 -160
- package/clis/hupu/reply.js +0 -72
- package/clis/hupu/search.js +0 -60
- package/clis/hupu/unlike.js +0 -76
- package/clis/hupu/utils.js +0 -319
- package/clis/jd/add-cart.js +0 -72
- package/clis/jd/auth.js +0 -46
- package/clis/jd/cart.js +0 -80
- package/clis/jd/commands.test.js +0 -41
- package/clis/jd/detail.js +0 -63
- package/clis/jd/item.js +0 -730
- package/clis/jd/item.test.js +0 -354
- package/clis/jd/item.test.ts +0 -517
- package/clis/jd/reviews.js +0 -55
- package/clis/jd/search.js +0 -66
- package/clis/jianyu/auth.js +0 -62
- package/clis/jianyu/detail.js +0 -21
- package/clis/jianyu/search.js +0 -634
- package/clis/jianyu/search.test.js +0 -153
- package/clis/jianyu/shared/china-bid-search.js +0 -165
- package/clis/jianyu/shared/procurement-contract.js +0 -324
- package/clis/jianyu/shared/procurement-contract.test.js +0 -72
- package/clis/jianyu/shared/procurement-detail.js +0 -107
- package/clis/jianyu/shared/procurement-detail.test.js +0 -84
- package/clis/jike/auth.js +0 -48
- package/clis/jike/comment.js +0 -107
- package/clis/jike/create.js +0 -106
- package/clis/jike/feed.js +0 -68
- package/clis/jike/like.js +0 -61
- package/clis/jike/notifications.js +0 -169
- package/clis/jike/post.js +0 -72
- package/clis/jike/read.test.js +0 -86
- package/clis/jike/repost.js +0 -103
- package/clis/jike/search.js +0 -68
- package/clis/jike/topic.js +0 -65
- package/clis/jike/user.js +0 -65
- package/clis/jike/utils.js +0 -25
- package/clis/jimeng/auth.js +0 -46
- package/clis/jimeng/generate.js +0 -84
- package/clis/jimeng/history.js +0 -48
- package/clis/jimeng/new.js +0 -44
- package/clis/jimeng/workspaces.js +0 -42
- package/clis/juejin/hot.js +0 -37
- package/clis/juejin/juejin.test.js +0 -241
- package/clis/juejin/recommend.js +0 -61
- package/clis/juejin/utils.js +0 -199
- package/clis/ke/auth.js +0 -46
- package/clis/ke/chengjiao.js +0 -78
- package/clis/ke/ershoufang.js +0 -101
- package/clis/ke/utils.js +0 -104
- package/clis/ke/xiaoqu.js +0 -78
- package/clis/ke/zufang.js +0 -95
- package/clis/kimi/_utils.js +0 -110
- package/clis/kimi/audit-extras.js +0 -268
- package/clis/kimi/auth.js +0 -57
- package/clis/kimi/chat.js +0 -470
- package/clis/kimi/kimi.test.js +0 -187
- package/clis/kimi/storage.js +0 -169
- package/clis/kimi/ui.js +0 -314
- package/clis/kimi/usage.js +0 -144
- package/clis/linux-do/auth.js +0 -55
- package/clis/linux-do/categories.js +0 -50
- package/clis/linux-do/feed.js +0 -395
- package/clis/linux-do/feed.test.js +0 -153
- package/clis/linux-do/search.js +0 -29
- package/clis/linux-do/tags.js +0 -28
- package/clis/linux-do/topic-content.js +0 -155
- package/clis/linux-do/topic-content.test.js +0 -59
- package/clis/linux-do/topic.js +0 -53
- package/clis/linux-do/user-posts.js +0 -57
- package/clis/linux-do/user-topics.js +0 -37
- package/clis/maimai/auth.js +0 -47
- package/clis/maimai/search-talents.js +0 -192
- package/clis/maimai/search-talents.test.js +0 -29
- package/clis/mubu/doc.js +0 -41
- package/clis/mubu/docs.js +0 -44
- package/clis/mubu/notes.js +0 -245
- package/clis/mubu/recent.js +0 -28
- package/clis/mubu/search.js +0 -63
- package/clis/mubu/utils.js +0 -304
- package/clis/nowcoder/auth.js +0 -65
- package/clis/nowcoder/companies.js +0 -24
- package/clis/nowcoder/creators.js +0 -28
- package/clis/nowcoder/detail.js +0 -62
- package/clis/nowcoder/experience.js +0 -37
- package/clis/nowcoder/hot.js +0 -25
- package/clis/nowcoder/jobs.js +0 -22
- package/clis/nowcoder/notifications.js +0 -30
- package/clis/nowcoder/papers.js +0 -41
- package/clis/nowcoder/practice.js +0 -38
- package/clis/nowcoder/recommend.js +0 -31
- package/clis/nowcoder/referral.js +0 -40
- package/clis/nowcoder/salary.js +0 -41
- package/clis/nowcoder/search.js +0 -50
- package/clis/nowcoder/suggest.js +0 -34
- package/clis/nowcoder/topics.js +0 -28
- package/clis/nowcoder/trending.js +0 -26
- package/clis/ones/common.js +0 -144
- package/clis/ones/enrich-tasks.js +0 -37
- package/clis/ones/login.js +0 -81
- package/clis/ones/logout.js +0 -18
- package/clis/ones/me.js +0 -31
- package/clis/ones/my-tasks.js +0 -121
- package/clis/ones/resolve-labels.js +0 -64
- package/clis/ones/task-helpers.js +0 -212
- package/clis/ones/task-helpers.test.js +0 -12
- package/clis/ones/task.js +0 -67
- package/clis/ones/tasks.js +0 -80
- package/clis/ones/token-info.js +0 -43
- package/clis/ones/worklog.js +0 -268
- package/clis/ones/worklog.test.js +0 -20
- package/clis/powerchina/auth.js +0 -52
- package/clis/powerchina/search.js +0 -251
- package/clis/powerchina/search.test.js +0 -93
- package/clis/quark/auth.js +0 -47
- package/clis/quark/ls.js +0 -64
- package/clis/quark/mkdir.js +0 -37
- package/clis/quark/mv.js +0 -54
- package/clis/quark/rename.js +0 -27
- package/clis/quark/rm.js +0 -25
- package/clis/quark/save.js +0 -81
- package/clis/quark/share-tree.js +0 -46
- package/clis/quark/utils.js +0 -146
- package/clis/quark/utils.test.js +0 -58
- package/clis/qwen/ask.js +0 -104
- package/clis/qwen/ask.test.js +0 -36
- package/clis/qwen/auth.js +0 -56
- package/clis/qwen/detail.js +0 -62
- package/clis/qwen/history.js +0 -61
- package/clis/qwen/image.js +0 -179
- package/clis/qwen/new.js +0 -23
- package/clis/qwen/read.js +0 -41
- package/clis/qwen/send.js +0 -55
- package/clis/qwen/status.js +0 -37
- package/clis/qwen/utils.js +0 -461
- package/clis/qwen/utils.test.js +0 -128
- package/clis/rednote/auth.js +0 -52
- package/clis/rednote/comments.js +0 -85
- package/clis/rednote/download.js +0 -59
- package/clis/rednote/feed.js +0 -27
- package/clis/rednote/navigation.test.js +0 -26
- package/clis/rednote/note.js +0 -68
- package/clis/rednote/notifications.js +0 -139
- package/clis/rednote/rednote.test.js +0 -240
- package/clis/rednote/search.js +0 -107
- package/clis/rednote/user.js +0 -55
- package/clis/sinablog/article.js +0 -15
- package/clis/sinablog/hot.js +0 -15
- package/clis/sinablog/search.js +0 -52
- package/clis/sinablog/user.js +0 -16
- package/clis/sinablog/utils.js +0 -186
- package/clis/sinafinance/news.js +0 -62
- package/clis/sinafinance/rolling-news.js +0 -41
- package/clis/sinafinance/stock-rank.js +0 -66
- package/clis/sinafinance/stock.js +0 -121
- package/clis/sinafinance/stock.test.js +0 -59
- package/clis/smzdm/search.js +0 -149
- package/clis/smzdm/search.test.js +0 -114
- package/clis/taobao/add-cart.js +0 -150
- package/clis/taobao/auth.js +0 -58
- package/clis/taobao/cart.js +0 -96
- package/clis/taobao/commands.test.js +0 -41
- package/clis/taobao/detail.js +0 -71
- package/clis/taobao/reviews.js +0 -77
- package/clis/taobao/search.js +0 -97
- package/clis/tdx/hot-rank.js +0 -48
- package/clis/tdx/hot-rank.test.js +0 -59
- package/clis/ths/hot-rank.js +0 -50
- package/clis/ths/hot-rank.test.js +0 -64
- package/clis/tieba/commands.test.js +0 -79
- package/clis/tieba/hot.js +0 -48
- package/clis/tieba/posts.js +0 -86
- package/clis/tieba/read.js +0 -141
- package/clis/tieba/search.js +0 -109
- package/clis/tieba/utils.js +0 -240
- package/clis/tieba/utils.test.js +0 -290
- package/clis/toutiao/articles.js +0 -55
- package/clis/toutiao/auth.js +0 -70
- package/clis/toutiao/hot.js +0 -63
- package/clis/toutiao/toutiao.test.js +0 -378
- package/clis/toutiao/utils.js +0 -161
- package/clis/trae-cn/activity.js +0 -22
- package/clis/trae-cn/approve.js +0 -52
- package/clis/trae-cn/ask.js +0 -99
- package/clis/trae-cn/dump.js +0 -5
- package/clis/trae-cn/export.js +0 -40
- package/clis/trae-cn/model.js +0 -22
- package/clis/trae-cn/new.js +0 -63
- package/clis/trae-cn/read.js +0 -23
- package/clis/trae-cn/screenshot.js +0 -5
- package/clis/trae-cn/select-model.js +0 -25
- package/clis/trae-cn/send.js +0 -23
- package/clis/trae-cn/setup.js +0 -75
- package/clis/trae-cn/status.js +0 -27
- package/clis/trae-cn/targets.js +0 -96
- package/clis/trae-cn/trae-cn.test.js +0 -412
- package/clis/trae-cn/utils.js +0 -750
- package/clis/trae-cn/watch.js +0 -119
- package/clis/uisdc/news.js +0 -105
- package/clis/uisdc/news.test.js +0 -66
- package/clis/v2ex/auth.js +0 -44
- package/clis/v2ex/daily.js +0 -99
- package/clis/v2ex/hot.js +0 -26
- package/clis/v2ex/latest.js +0 -26
- package/clis/v2ex/me.js +0 -100
- package/clis/v2ex/member.js +0 -28
- package/clis/v2ex/node.js +0 -39
- package/clis/v2ex/nodes.js +0 -26
- package/clis/v2ex/notifications.js +0 -72
- package/clis/v2ex/replies.js +0 -27
- package/clis/v2ex/topic.js +0 -31
- package/clis/v2ex/user.js +0 -34
- package/clis/wanfang/search.js +0 -66
- package/clis/wanfang/search.test.js +0 -23
- package/clis/wechat-channels/auth.js +0 -62
- package/clis/wechat-channels/publish.js +0 -732
- package/clis/wechat-channels/publish.test.js +0 -144
- package/clis/weibo/auth.js +0 -53
- package/clis/weibo/comments.js +0 -53
- package/clis/weibo/delete.js +0 -172
- package/clis/weibo/delete.test.js +0 -94
- package/clis/weibo/envelope.test.js +0 -85
- package/clis/weibo/favorites.js +0 -170
- package/clis/weibo/favorites.test.js +0 -114
- package/clis/weibo/feed.js +0 -69
- package/clis/weibo/hot.js +0 -39
- package/clis/weibo/me.js +0 -75
- package/clis/weibo/post.js +0 -75
- package/clis/weibo/publish.js +0 -306
- package/clis/weibo/publish.test.js +0 -192
- package/clis/weibo/search.js +0 -80
- package/clis/weibo/user-posts.js +0 -234
- package/clis/weibo/user-posts.test.js +0 -92
- package/clis/weibo/user.js +0 -63
- package/clis/weibo/utils.js +0 -59
- package/clis/weibo/utils.test.js +0 -36
- package/clis/weixin/create-draft.js +0 -226
- package/clis/weixin/download.js +0 -371
- package/clis/weixin/download.test.js +0 -106
- package/clis/weixin/drafts.js +0 -66
- package/clis/weixin/drafts.test.js +0 -69
- package/clis/weixin/search.js +0 -157
- package/clis/weixin/search.test.js +0 -227
- package/clis/weread/ai-outline.js +0 -171
- package/clis/weread/ai-outline.test.js +0 -83
- package/clis/weread/auth.js +0 -59
- package/clis/weread/book-search.js +0 -438
- package/clis/weread/book-search.test.js +0 -242
- package/clis/weread/book.js +0 -233
- package/clis/weread/commands.test.js +0 -398
- package/clis/weread/highlights.js +0 -24
- package/clis/weread/notebooks.js +0 -22
- package/clis/weread/notes.js +0 -30
- package/clis/weread/private-api-regression.test.js +0 -369
- package/clis/weread/ranking.js +0 -29
- package/clis/weread/search-regression.test.js +0 -494
- package/clis/weread/search.js +0 -186
- package/clis/weread/shelf.js +0 -66
- package/clis/weread/utils.js +0 -321
- package/clis/weread/utils.test.js +0 -128
- package/clis/weread-official/book.js +0 -135
- package/clis/weread-official/commands.test.js +0 -385
- package/clis/weread-official/discover.js +0 -107
- package/clis/weread-official/list-apis.js +0 -95
- package/clis/weread-official/notes.js +0 -171
- package/clis/weread-official/readdata.js +0 -158
- package/clis/weread-official/review.js +0 -93
- package/clis/weread-official/search.js +0 -106
- package/clis/weread-official/shelf.js +0 -97
- package/clis/weread-official/utils.js +0 -293
- package/clis/weread-official/utils.test.js +0 -242
- package/clis/xianyu/auth.js +0 -68
- package/clis/xianyu/chat.js +0 -74
- package/clis/xianyu/chat.test.js +0 -84
- package/clis/xianyu/im.js +0 -322
- package/clis/xianyu/im.test.js +0 -253
- package/clis/xianyu/inbox.js +0 -96
- package/clis/xianyu/item.js +0 -153
- package/clis/xianyu/item.test.js +0 -56
- package/clis/xianyu/messages.js +0 -91
- package/clis/xianyu/publish.js +0 -485
- package/clis/xianyu/publish.test.js +0 -220
- package/clis/xianyu/reply.js +0 -82
- package/clis/xianyu/search.js +0 -262
- package/clis/xianyu/search.test.js +0 -104
- package/clis/xianyu/utils.js +0 -8
- package/clis/xiaoe/auth.js +0 -55
- package/clis/xiaoe/catalog.js +0 -227
- package/clis/xiaoe/content.js +0 -175
- package/clis/xiaoe/courses.js +0 -127
- package/clis/xiaoe/detail.js +0 -36
- package/clis/xiaoe/play-url.js +0 -121
- package/clis/xiaoe/xiaoe.test.js +0 -486
- package/clis/xiaohongshu/ask.js +0 -426
- package/clis/xiaohongshu/ask.test.js +0 -271
- package/clis/xiaohongshu/auth.js +0 -53
- package/clis/xiaohongshu/collection-helpers.js +0 -283
- package/clis/xiaohongshu/collection-helpers.test.js +0 -125
- package/clis/xiaohongshu/comments.js +0 -228
- package/clis/xiaohongshu/comments.test.js +0 -320
- package/clis/xiaohongshu/creator-note-detail.js +0 -482
- package/clis/xiaohongshu/creator-note-detail.test.js +0 -451
- package/clis/xiaohongshu/creator-notes-summary.js +0 -98
- package/clis/xiaohongshu/creator-notes-summary.test.js +0 -94
- package/clis/xiaohongshu/creator-notes.js +0 -479
- package/clis/xiaohongshu/creator-notes.test.js +0 -281
- package/clis/xiaohongshu/creator-profile.js +0 -55
- package/clis/xiaohongshu/creator-stats.js +0 -76
- package/clis/xiaohongshu/creator-stats.test.js +0 -24
- package/clis/xiaohongshu/delete-note.js +0 -260
- package/clis/xiaohongshu/delete-note.test.js +0 -172
- package/clis/xiaohongshu/download.js +0 -249
- package/clis/xiaohongshu/download.test.js +0 -339
- package/clis/xiaohongshu/draft-clear.js +0 -91
- package/clis/xiaohongshu/draft-delete.js +0 -103
- package/clis/xiaohongshu/draft-open.js +0 -43
- package/clis/xiaohongshu/draft-utils.js +0 -142
- package/clis/xiaohongshu/drafts.js +0 -28
- package/clis/xiaohongshu/drafts.test.js +0 -172
- package/clis/xiaohongshu/feed.js +0 -163
- package/clis/xiaohongshu/feed.test.js +0 -126
- package/clis/xiaohongshu/follow.js +0 -226
- package/clis/xiaohongshu/follow.test.js +0 -211
- package/clis/xiaohongshu/liked.js +0 -29
- package/clis/xiaohongshu/liked.test.js +0 -82
- package/clis/xiaohongshu/navigation.test.js +0 -38
- package/clis/xiaohongshu/note-helpers.js +0 -69
- package/clis/xiaohongshu/note.js +0 -104
- package/clis/xiaohongshu/note.test.js +0 -249
- package/clis/xiaohongshu/notifications.js +0 -46
- package/clis/xiaohongshu/publish.js +0 -1423
- package/clis/xiaohongshu/publish.test.js +0 -1141
- package/clis/xiaohongshu/saved.js +0 -29
- package/clis/xiaohongshu/saved.test.js +0 -143
- package/clis/xiaohongshu/search.js +0 -330
- package/clis/xiaohongshu/search.test.js +0 -325
- package/clis/xiaohongshu/unfollow.js +0 -285
- package/clis/xiaohongshu/user-helpers.js +0 -78
- package/clis/xiaohongshu/user-helpers.test.js +0 -160
- package/clis/xiaohongshu/user.js +0 -140
- package/clis/xiaohongshu/user.test.js +0 -141
- package/clis/xiaoyuzhou/auth.js +0 -303
- package/clis/xiaoyuzhou/auth.test.js +0 -124
- package/clis/xiaoyuzhou/download.js +0 -54
- package/clis/xiaoyuzhou/download.test.js +0 -135
- package/clis/xiaoyuzhou/episode.js +0 -34
- package/clis/xiaoyuzhou/podcast-episodes.js +0 -41
- package/clis/xiaoyuzhou/podcast-episodes.test.js +0 -78
- package/clis/xiaoyuzhou/podcast.js +0 -33
- package/clis/xiaoyuzhou/transcript.js +0 -77
- package/clis/xiaoyuzhou/transcript.test.js +0 -195
- package/clis/xiaoyuzhou/utils.js +0 -15
- package/clis/xiaoyuzhou/utils.test.js +0 -39
- package/clis/xueqiu/auth.js +0 -63
- package/clis/xueqiu/comments.js +0 -355
- package/clis/xueqiu/comments.test.js +0 -696
- package/clis/xueqiu/danjuan-utils.js +0 -126
- package/clis/xueqiu/danjuan-utils.test.js +0 -41
- package/clis/xueqiu/earnings-date.js +0 -45
- package/clis/xueqiu/feed.js +0 -39
- package/clis/xueqiu/fund-holdings.js +0 -29
- package/clis/xueqiu/fund-snapshot.js +0 -26
- package/clis/xueqiu/groups.js +0 -23
- package/clis/xueqiu/hot-stock.js +0 -32
- package/clis/xueqiu/hot.js +0 -37
- package/clis/xueqiu/kline.js +0 -44
- package/clis/xueqiu/search.js +0 -37
- package/clis/xueqiu/stock.js +0 -65
- package/clis/xueqiu/utils.js +0 -48
- package/clis/xueqiu/utils.test.js +0 -26
- package/clis/xueqiu/watchlist.js +0 -37
- package/clis/youdao/note.js +0 -258
- package/clis/youdao/note.test.js +0 -99
- package/clis/yuanbao/ask.js +0 -362
- package/clis/yuanbao/ask.test.js +0 -124
- package/clis/yuanbao/auth.js +0 -59
- package/clis/yuanbao/detail.js +0 -65
- package/clis/yuanbao/history.js +0 -51
- package/clis/yuanbao/new.js +0 -72
- package/clis/yuanbao/new.test.js +0 -30
- package/clis/yuanbao/read.js +0 -38
- package/clis/yuanbao/send.js +0 -57
- package/clis/yuanbao/shared.js +0 -341
- package/clis/yuanbao/shared.test.js +0 -80
- package/clis/yuanbao/status.js +0 -44
- package/clis/zhihu/answer-comments.js +0 -280
- package/clis/zhihu/answer-comments.test.js +0 -287
- package/clis/zhihu/answer-detail.js +0 -216
- package/clis/zhihu/answer-detail.test.js +0 -338
- package/clis/zhihu/answer.js +0 -54
- package/clis/zhihu/answer.test.js +0 -54
- package/clis/zhihu/auth.js +0 -59
- package/clis/zhihu/collection.js +0 -199
- package/clis/zhihu/collection.test.js +0 -337
- package/clis/zhihu/collections.js +0 -128
- package/clis/zhihu/collections.test.js +0 -182
- package/clis/zhihu/comment.js +0 -55
- package/clis/zhihu/comment.test.js +0 -50
- package/clis/zhihu/download.js +0 -80
- package/clis/zhihu/download.test.js +0 -12
- package/clis/zhihu/favorite.js +0 -87
- package/clis/zhihu/favorite.test.js +0 -59
- package/clis/zhihu/follow.js +0 -50
- package/clis/zhihu/follow.test.js +0 -42
- package/clis/zhihu/followers.js +0 -40
- package/clis/zhihu/followers.test.js +0 -42
- package/clis/zhihu/following.js +0 -40
- package/clis/zhihu/following.test.js +0 -44
- package/clis/zhihu/hot.js +0 -44
- package/clis/zhihu/like.js +0 -47
- package/clis/zhihu/like.test.js +0 -41
- package/clis/zhihu/paginate.js +0 -128
- package/clis/zhihu/pins.js +0 -41
- package/clis/zhihu/pins.test.js +0 -44
- package/clis/zhihu/question.js +0 -113
- package/clis/zhihu/question.test.js +0 -251
- package/clis/zhihu/recommend.js +0 -103
- package/clis/zhihu/recommend.test.js +0 -143
- package/clis/zhihu/search.js +0 -206
- package/clis/zhihu/search.test.js +0 -198
- package/clis/zhihu/target.js +0 -91
- package/clis/zhihu/target.test.js +0 -77
- package/clis/zhihu/text.js +0 -29
- package/clis/zhihu/text.test.js +0 -24
- package/clis/zhihu/user-answers.js +0 -41
- package/clis/zhihu/user-answers.test.js +0 -69
- package/clis/zhihu/user-arg.js +0 -34
- package/clis/zhihu/user-articles.js +0 -40
- package/clis/zhihu/user-articles.test.js +0 -44
- package/clis/zhihu/user.js +0 -59
- package/clis/zhihu/user.test.js +0 -73
- package/clis/zhihu/write-shared.js +0 -228
- package/clis/zhihu/write-shared.test.js +0 -176
- package/clis/zsxq/auth.js +0 -68
- package/clis/zsxq/dynamics.js +0 -48
- package/clis/zsxq/groups.js +0 -33
- package/clis/zsxq/search.js +0 -44
- package/clis/zsxq/search.test.js +0 -24
- package/clis/zsxq/topic.js +0 -50
- package/clis/zsxq/topic.test.js +0 -30
- package/clis/zsxq/topics.js +0 -26
- package/clis/zsxq/topics.test.js +0 -24
- package/clis/zsxq/utils.js +0 -233
- package/clis/zsxq/utils.test.js +0 -31
- package/dist/src/weixin-download.test.js +0 -45
- package/skills/webcmd-adapter-author/references/site-memory/bilibili.md +0 -70
- package/skills/webcmd-adapter-author/references/site-memory/eastmoney.md +0 -70
- package/skills/webcmd-adapter-author/references/site-memory/tonghuashun.md +0 -51
- package/skills/webcmd-adapter-author/references/site-memory/xueqiu.md +0 -58
- /package/{clis/douyin/_shared/types.js → dist/src/generate-release-notes-cli.test.d.ts} +0 -0
- /package/dist/src/{weixin-download.test.d.ts → release-notes.test.d.ts} +0 -0
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { getRegistry } from '@agentrhq/webcmd/registry';
|
|
3
|
-
import './publish.js';
|
|
4
|
-
describe('douyin publish registration', () => {
|
|
5
|
-
it('registers the publish command', () => {
|
|
6
|
-
const registry = getRegistry();
|
|
7
|
-
const cmds = [...registry.values()];
|
|
8
|
-
const cmd = cmds.find((c) => c.site === 'douyin' && c.name === 'publish');
|
|
9
|
-
expect(cmd).toBeDefined();
|
|
10
|
-
expect(cmd?.args.some((a) => a.name === 'video')).toBe(true);
|
|
11
|
-
expect(cmd?.args.some((a) => a.name === 'title')).toBe(true);
|
|
12
|
-
expect(cmd?.args.some((a) => a.name === 'schedule')).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
it('has all expected args', () => {
|
|
15
|
-
const registry = getRegistry();
|
|
16
|
-
const cmd = [...registry.values()].find((c) => c.site === 'douyin' && c.name === 'publish');
|
|
17
|
-
const argNames = cmd?.args.map((a) => a.name) ?? [];
|
|
18
|
-
expect(argNames).toContain('video');
|
|
19
|
-
expect(argNames).toContain('title');
|
|
20
|
-
expect(argNames).toContain('schedule');
|
|
21
|
-
expect(argNames).toContain('caption');
|
|
22
|
-
expect(argNames).toContain('cover');
|
|
23
|
-
expect(argNames).toContain('visibility');
|
|
24
|
-
expect(argNames).toContain('allow_download');
|
|
25
|
-
expect(argNames).toContain('collection');
|
|
26
|
-
expect(argNames).toContain('activity');
|
|
27
|
-
expect(argNames).toContain('poi_id');
|
|
28
|
-
expect(argNames).toContain('poi_name');
|
|
29
|
-
expect(argNames).toContain('hotspot');
|
|
30
|
-
expect(argNames).toContain('no_safety_check');
|
|
31
|
-
expect(argNames).toContain('sync_toutiao');
|
|
32
|
-
});
|
|
33
|
-
it('uses COOKIE strategy', () => {
|
|
34
|
-
const registry = getRegistry();
|
|
35
|
-
const cmd = [...registry.values()].find((c) => c.site === 'douyin' && c.name === 'publish');
|
|
36
|
-
expect(cmd?.strategy).toBe('cookie');
|
|
37
|
-
});
|
|
38
|
-
});
|
package/clis/douyin/search.js
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Douyin search — keyword video search on www.douyin.com.
|
|
3
|
-
*
|
|
4
|
-
* Strategy: DOM extraction from the server-rendered search results page.
|
|
5
|
-
*
|
|
6
|
-
* Why not XHR interception:
|
|
7
|
-
* The `www.douyin.com/search/<q>?type=video` page renders results into
|
|
8
|
-
* `<ul data-e2e="scroll-list">` server-side during initial navigation
|
|
9
|
-
* and (for the Webcmd-bridged browser context) does NOT fire a
|
|
10
|
-
* subsequent `/aweme/v1/web/general/search/single/` XHR — we confirmed
|
|
11
|
-
* this by `wait xhr "general/search/single"` timing out at 20s on a
|
|
12
|
-
* logged-in profile that has visible result cards in the DOM. Direct
|
|
13
|
-
* synthesis of the XHR from page context returns
|
|
14
|
-
* `status_code: 0, data: [], search_nil_info: { search_nil_type:
|
|
15
|
-
* "verify_check" }` because the bare URL lacks the SPA-computed
|
|
16
|
-
* `a_bogus` / `msToken` signature.
|
|
17
|
-
*
|
|
18
|
-
* DOM extraction sidesteps both blockers: the data is already in the
|
|
19
|
-
* rendered HTML at the moment of navigation, signature-free.
|
|
20
|
-
*
|
|
21
|
-
* Selector approach:
|
|
22
|
-
* Douyin obfuscates card classnames (e.g. `.ckopQfVu`, `.cIiU4Muu`)
|
|
23
|
-
* and they churn between builds. We pin only the stable hooks:
|
|
24
|
-
* - container: `[data-e2e="scroll-list"]`
|
|
25
|
-
* - row: `li` inside the container
|
|
26
|
-
* - url: `a[href*="/video/"]`
|
|
27
|
-
* - other fields are extracted from the row's leaf text nodes by
|
|
28
|
-
* SHAPE (digit+万/亿 → likes; HH:MM or MM:SS → duration; text after
|
|
29
|
-
* `@` → author nickname; longest remaining → desc).
|
|
30
|
-
*
|
|
31
|
-
* Output fields mirror `tiktok search` (rank, desc, author, url, plays,
|
|
32
|
-
* likes, comments, shares) so downstream tools that already normalize
|
|
33
|
-
* tiktok rows can consume douyin rows without per-adapter glue. The
|
|
34
|
-
* search results page only surfaces the like count — plays/comments/
|
|
35
|
-
* shares are not in the card markup and we expose them as 0 rather
|
|
36
|
-
* than fabricate values; clients that need them should fetch
|
|
37
|
-
* /aweme/v1/web/aweme/detail/?aweme_id=... for the relevant id.
|
|
38
|
-
*
|
|
39
|
-
* Prerequisite: the bound Chrome profile must be logged in to
|
|
40
|
-
* https://www.douyin.com. The search results page renders an empty
|
|
41
|
-
* skeleton for anonymous visitors, which we surface as AuthRequiredError.
|
|
42
|
-
*/
|
|
43
|
-
import { cli, Strategy } from '@agentrhq/webcmd/registry';
|
|
44
|
-
import { ArgumentError, AuthRequiredError, CommandExecutionError, EmptyResultError } from '@agentrhq/webcmd/errors';
|
|
45
|
-
|
|
46
|
-
export const MAX_SEARCH_LIMIT = 30;
|
|
47
|
-
// Time budget for the SPA's initial DOM commit. Empirically the
|
|
48
|
-
// scroll-list `<li>` rows appear within 2-4s of navigation when logged
|
|
49
|
-
// in; 15s covers slow networks without blocking on a permanently-empty
|
|
50
|
-
// page (anonymous gate, network error).
|
|
51
|
-
export const RENDER_TIMEOUT_MS = 15000;
|
|
52
|
-
|
|
53
|
-
export function parseSearchLimit(raw) {
|
|
54
|
-
const parsed = Number(raw ?? 10);
|
|
55
|
-
if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
|
|
56
|
-
throw new ArgumentError(`--limit must be an integer between 1 and ${MAX_SEARCH_LIMIT}, got ${JSON.stringify(raw)}`);
|
|
57
|
-
}
|
|
58
|
-
if (parsed < 1 || parsed > MAX_SEARCH_LIMIT) {
|
|
59
|
-
throw new ArgumentError(`--limit must be between 1 and ${MAX_SEARCH_LIMIT}, got ${parsed}`);
|
|
60
|
-
}
|
|
61
|
-
return parsed;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Parse a Douyin display count like "1.9万", "3.1万", "4702", "1.2亿"
|
|
66
|
-
* into a plain integer. Returns 0 for unparseable input rather than
|
|
67
|
-
* throwing — the CLI promises numeric columns and missing data is
|
|
68
|
-
* common enough on real result rows that a soft fallback is the right
|
|
69
|
-
* choice.
|
|
70
|
-
*/
|
|
71
|
-
export function parseDouyinCount(text) {
|
|
72
|
-
if (typeof text !== 'string') return 0;
|
|
73
|
-
const m = text.replace(/\s/g, '').match(/^(\d+(?:\.\d+)?)([万亿])?$/);
|
|
74
|
-
if (!m) {
|
|
75
|
-
const plain = Number(text.replace(/[,\s]/g, ''));
|
|
76
|
-
return Number.isFinite(plain) ? Math.round(plain) : 0;
|
|
77
|
-
}
|
|
78
|
-
const n = Number(m[1]);
|
|
79
|
-
if (!Number.isFinite(n)) return 0;
|
|
80
|
-
if (m[2] === '万') return Math.round(n * 10_000);
|
|
81
|
-
if (m[2] === '亿') return Math.round(n * 100_000_000);
|
|
82
|
-
return Math.round(n);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function extractDouyinVideoId(href) {
|
|
86
|
-
if (typeof href !== 'string' || !href) return '';
|
|
87
|
-
let full = href;
|
|
88
|
-
if (full.startsWith('//')) full = 'https:' + full;
|
|
89
|
-
else if (full.startsWith('/')) full = 'https://www.douyin.com' + full;
|
|
90
|
-
try {
|
|
91
|
-
const parsed = new URL(full);
|
|
92
|
-
if (!/(^|\.)douyin\.com$/.test(parsed.hostname)) return '';
|
|
93
|
-
const match = parsed.pathname.match(/^\/video\/(\d+)$/);
|
|
94
|
-
return match?.[1] ?? '';
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return '';
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Resolve scheme-relative or absolute Douyin video links to the canonical
|
|
103
|
-
* https://www.douyin.com/video/<id> shape. Returns '' for unparseable
|
|
104
|
-
* input rather than throwing — callers expect a string column.
|
|
105
|
-
*/
|
|
106
|
-
export function normalizeDouyinVideoUrl(href) {
|
|
107
|
-
const id = extractDouyinVideoId(href);
|
|
108
|
-
return id ? `https://www.douyin.com/video/${id}` : '';
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function isSearchCardMetadataText(text) {
|
|
112
|
-
if (!text) return true;
|
|
113
|
-
if (/^\d{1,2}:\d{2}(?::\d{2})?$/.test(text)) return true;
|
|
114
|
-
if (/^\d+(?:\.\d+)?[万亿]?$/.test(text)) return true;
|
|
115
|
-
if (/^(合集|视频|作者)$/.test(text)) return true;
|
|
116
|
-
if (/^(刚刚|今天|昨天|前天)$/.test(text)) return true;
|
|
117
|
-
if (/^\d+\s*(秒|分钟|小时|天|周|个月|月|年)前$/.test(text)) return true;
|
|
118
|
-
if (/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/.test(text)) return true;
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Project a single rendered card into the canonical row shape. Operates
|
|
124
|
-
* on a serialized card payload (the raw `{href, leafTexts}` we collect
|
|
125
|
-
* via page.evaluate) so this function is unit-testable without a real
|
|
126
|
-
* browser.
|
|
127
|
-
*
|
|
128
|
-
* `leafTexts` is the ordered list of `textContent.trim()` for every leaf
|
|
129
|
-
* element inside the card (no children). The fields we want are
|
|
130
|
-
* identified by shape:
|
|
131
|
-
* - duration: matches `HH:MM:SS` or `MM:SS`
|
|
132
|
-
* - likes: matches `<digits>(.<digits>)?(万|亿)?` and ISN'T the duration
|
|
133
|
-
* - author: the text node immediately following an `@` text node
|
|
134
|
-
* - desc: the longest remaining leaf text
|
|
135
|
-
*/
|
|
136
|
-
export function projectCard(card, index) {
|
|
137
|
-
const url = normalizeDouyinVideoUrl(card?.url ?? card?.href);
|
|
138
|
-
const texts = Array.isArray(card?.leafTexts) ? card.leafTexts.map((t) => String(t ?? '').trim()).filter(Boolean) : [];
|
|
139
|
-
|
|
140
|
-
const DURATION_RE = /^\d{1,2}:\d{2}(?::\d{2})?$/;
|
|
141
|
-
const COUNT_RE = /^\d+(?:\.\d+)?[万亿]?$/;
|
|
142
|
-
|
|
143
|
-
let likes = 0;
|
|
144
|
-
let author = '';
|
|
145
|
-
let longest = '';
|
|
146
|
-
|
|
147
|
-
for (let i = 0; i < texts.length; i++) {
|
|
148
|
-
const t = texts[i];
|
|
149
|
-
if (DURATION_RE.test(t)) continue;
|
|
150
|
-
if (!likes && COUNT_RE.test(t)) {
|
|
151
|
-
likes = parseDouyinCount(t);
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (t === '@' && !author) {
|
|
155
|
-
author = (texts[i + 1] ?? '').trim();
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
if (t === author) continue;
|
|
159
|
-
if (isSearchCardMetadataText(t)) continue;
|
|
160
|
-
if (t.length > longest.length) longest = t;
|
|
161
|
-
}
|
|
162
|
-
let desc = longest;
|
|
163
|
-
// Strip a leading "@author" that some renders fuse into the desc text node.
|
|
164
|
-
if (author && desc.startsWith('@' + author)) {
|
|
165
|
-
desc = desc.slice(author.length + 1).trim();
|
|
166
|
-
}
|
|
167
|
-
return {
|
|
168
|
-
rank: index + 1,
|
|
169
|
-
desc,
|
|
170
|
-
author,
|
|
171
|
-
url,
|
|
172
|
-
plays: 0,
|
|
173
|
-
likes,
|
|
174
|
-
comments: 0,
|
|
175
|
-
shares: 0,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function isProjectedRowUsable(row) {
|
|
180
|
-
return Boolean(row?.url && row?.desc);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export function projectSearchCards(cards, limit) {
|
|
184
|
-
const window = Array.isArray(cards) ? cards.slice(0, limit) : [];
|
|
185
|
-
const rows = window.map((card, index) => projectCard(card, index));
|
|
186
|
-
const invalidCount = rows.filter((row) => !isProjectedRowUsable(row)).length;
|
|
187
|
-
return { rows: rows.filter(isProjectedRowUsable), invalidCount };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// JS snippet that waits for the scroll-list to populate, then returns
|
|
191
|
-
// `{state: 'rendered', cards}` or `{state: 'login_wall'}` /
|
|
192
|
-
// `{state: 'timeout'}`. Runs inside page.evaluate so we don't pay a
|
|
193
|
-
// round-trip per poll iteration.
|
|
194
|
-
const WAIT_AND_EXTRACT_JS = (timeoutMs) => `
|
|
195
|
-
new Promise((resolve) => {
|
|
196
|
-
const collectCards = () => {
|
|
197
|
-
const cards = [];
|
|
198
|
-
const lis = document.querySelectorAll('[data-e2e="scroll-list"] li');
|
|
199
|
-
for (const li of lis) {
|
|
200
|
-
const a = li.querySelector('a[href*="/video/"]');
|
|
201
|
-
if (!a) continue;
|
|
202
|
-
const leafTexts = [];
|
|
203
|
-
for (const el of li.querySelectorAll('*')) {
|
|
204
|
-
if (el.children.length > 0) continue;
|
|
205
|
-
const t = (el.textContent || '').trim();
|
|
206
|
-
if (t) leafTexts.push(t);
|
|
207
|
-
}
|
|
208
|
-
cards.push({ href: a.getAttribute('href') || '', leafTexts });
|
|
209
|
-
}
|
|
210
|
-
return cards;
|
|
211
|
-
};
|
|
212
|
-
const detectState = () => {
|
|
213
|
-
const cards = collectCards();
|
|
214
|
-
if (cards.length > 0) return { state: 'rendered', cards };
|
|
215
|
-
// Anonymous gate: Douyin renders a centered "登录后查看更多内容"
|
|
216
|
-
// overlay on /search/ for visitors without sessionid. Match either
|
|
217
|
-
// the literal Chinese prompt or a visible login modal/mask.
|
|
218
|
-
const text = (document.body && document.body.innerText) || '';
|
|
219
|
-
if (/登录后查看|请先登录|登录抖音|验证码|验证|verify_check|安全校验/.test(text)) return { state: 'login_wall' };
|
|
220
|
-
if (/暂无相关搜索结果|没有找到相关结果|搜索结果为空|暂无结果/.test(text)) return { state: 'empty' };
|
|
221
|
-
const modal = document.querySelector('[class*="login-mask"], [class*="LoginMask"], [class*="login-modal"], dialog[role="dialog"]');
|
|
222
|
-
if (modal && modal instanceof HTMLElement) {
|
|
223
|
-
const r = modal.getBoundingClientRect();
|
|
224
|
-
const s = getComputedStyle(modal);
|
|
225
|
-
if (r.width > 0 && r.height > 0 && s.display !== 'none' && s.visibility !== 'hidden') {
|
|
226
|
-
return { state: 'login_wall' };
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return null;
|
|
230
|
-
};
|
|
231
|
-
const found = detectState();
|
|
232
|
-
if (found) return resolve(found);
|
|
233
|
-
const observer = new MutationObserver(() => {
|
|
234
|
-
const s = detectState();
|
|
235
|
-
if (s) { observer.disconnect(); resolve(s); }
|
|
236
|
-
});
|
|
237
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
238
|
-
setTimeout(() => {
|
|
239
|
-
observer.disconnect();
|
|
240
|
-
const fallback = detectState();
|
|
241
|
-
resolve(fallback ?? { state: 'timeout' });
|
|
242
|
-
}, ${timeoutMs});
|
|
243
|
-
})
|
|
244
|
-
`;
|
|
245
|
-
|
|
246
|
-
function unwrapEvaluateResult(payload) {
|
|
247
|
-
if (payload && !Array.isArray(payload) && typeof payload === 'object' && 'session' in payload && 'data' in payload) {
|
|
248
|
-
return payload.data;
|
|
249
|
-
}
|
|
250
|
-
return payload;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
cli({
|
|
254
|
-
site: 'douyin',
|
|
255
|
-
name: 'search',
|
|
256
|
-
access: 'read',
|
|
257
|
-
description: '关键词搜索抖音视频',
|
|
258
|
-
domain: 'www.douyin.com',
|
|
259
|
-
strategy: Strategy.COOKIE,
|
|
260
|
-
args: [
|
|
261
|
-
{ name: 'query', required: true, positional: true, help: '搜索关键词' },
|
|
262
|
-
{ name: 'limit', type: 'int', default: 10, help: `结果数量 (1-${MAX_SEARCH_LIMIT})` },
|
|
263
|
-
],
|
|
264
|
-
columns: ['rank', 'desc', 'author', 'url', 'plays', 'likes', 'comments', 'shares'],
|
|
265
|
-
func: async (page, kwargs) => {
|
|
266
|
-
const limit = parseSearchLimit(kwargs.limit);
|
|
267
|
-
const keyword = String(kwargs.query ?? '').trim();
|
|
268
|
-
if (!keyword) {
|
|
269
|
-
throw new ArgumentError('douyin search 需要 <query> 关键词');
|
|
270
|
-
}
|
|
271
|
-
await page.goto(`https://www.douyin.com/search/${encodeURIComponent(keyword)}?type=video`);
|
|
272
|
-
let result;
|
|
273
|
-
try {
|
|
274
|
-
result = unwrapEvaluateResult(await page.evaluate(WAIT_AND_EXTRACT_JS(RENDER_TIMEOUT_MS)));
|
|
275
|
-
} catch (error) {
|
|
276
|
-
throw new CommandExecutionError(`Douyin search extraction failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
277
|
-
}
|
|
278
|
-
if (!result || typeof result !== 'object') {
|
|
279
|
-
throw new CommandExecutionError('Douyin search: unexpected evaluator payload shape');
|
|
280
|
-
}
|
|
281
|
-
if (result.state === 'login_wall') {
|
|
282
|
-
throw new AuthRequiredError(
|
|
283
|
-
'www.douyin.com',
|
|
284
|
-
'Douyin search results are blocked behind a login wall — log in at https://www.douyin.com in Chrome first.',
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
if (result.state === 'empty') {
|
|
288
|
-
throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);
|
|
289
|
-
}
|
|
290
|
-
if (result.state === 'timeout') {
|
|
291
|
-
throw new CommandExecutionError('Douyin search did not render result cards within the timeout. Open the same search in Chrome and verify login/security state before retrying.');
|
|
292
|
-
}
|
|
293
|
-
if (!Array.isArray(result.cards)) {
|
|
294
|
-
throw new CommandExecutionError('Douyin search: evaluator returned malformed cards payload');
|
|
295
|
-
}
|
|
296
|
-
if (result.cards.length === 0) {
|
|
297
|
-
throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);
|
|
298
|
-
}
|
|
299
|
-
const projected = projectSearchCards(result.cards, limit);
|
|
300
|
-
if (projected.invalidCount > 0) {
|
|
301
|
-
throw new CommandExecutionError('Douyin search parser found result cards without stable video url or description');
|
|
302
|
-
}
|
|
303
|
-
if (projected.rows.length === 0) {
|
|
304
|
-
throw new EmptyResultError('douyin search', `No Douyin videos matched "${keyword}".`);
|
|
305
|
-
}
|
|
306
|
-
return projected.rows;
|
|
307
|
-
},
|
|
308
|
-
});
|
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { getRegistry } from '@agentrhq/webcmd/registry';
|
|
3
|
-
import {
|
|
4
|
-
extractDouyinVideoId,
|
|
5
|
-
MAX_SEARCH_LIMIT,
|
|
6
|
-
normalizeDouyinVideoUrl,
|
|
7
|
-
parseDouyinCount,
|
|
8
|
-
parseSearchLimit,
|
|
9
|
-
projectCard,
|
|
10
|
-
projectSearchCards,
|
|
11
|
-
} from './search.js';
|
|
12
|
-
|
|
13
|
-
function createPageMock({ evaluateResult } = {}) {
|
|
14
|
-
return {
|
|
15
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
16
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
17
|
-
evaluate: vi.fn().mockResolvedValue(evaluateResult),
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe('douyin search', () => {
|
|
22
|
-
it('registers the command on www.douyin.com', () => {
|
|
23
|
-
const registry = getRegistry();
|
|
24
|
-
const cmd = [...registry.values()].find((c) => c.site === 'douyin' && c.name === 'search');
|
|
25
|
-
expect(cmd).toBeDefined();
|
|
26
|
-
expect(cmd?.domain).toBe('www.douyin.com');
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('rejects invalid limit before navigation', async () => {
|
|
30
|
-
const cmd = getRegistry().get('douyin/search');
|
|
31
|
-
const page = createPageMock();
|
|
32
|
-
await expect(cmd.func(page, { query: '咖啡', limit: 0 })).rejects.toMatchObject({
|
|
33
|
-
code: 'ARGUMENT',
|
|
34
|
-
message: expect.stringContaining('--limit'),
|
|
35
|
-
});
|
|
36
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
37
|
-
expect(page.evaluate).not.toHaveBeenCalled();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('rejects limit above MAX_SEARCH_LIMIT', () => {
|
|
41
|
-
expect(() => parseSearchLimit(MAX_SEARCH_LIMIT + 1)).toThrow(/--limit/);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('rejects an empty query', async () => {
|
|
45
|
-
const cmd = getRegistry().get('douyin/search');
|
|
46
|
-
const page = createPageMock();
|
|
47
|
-
await expect(cmd.func(page, { query: ' ', limit: 5 })).rejects.toMatchObject({
|
|
48
|
-
code: 'ARGUMENT',
|
|
49
|
-
});
|
|
50
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('returns ranked cards from the rendered scroll-list', async () => {
|
|
54
|
-
const cmd = getRegistry().get('douyin/search');
|
|
55
|
-
const page = createPageMock({
|
|
56
|
-
evaluateResult: {
|
|
57
|
-
state: 'rendered',
|
|
58
|
-
cards: [
|
|
59
|
-
{
|
|
60
|
-
url: '//www.douyin.com/video/7585120459717365001',
|
|
61
|
-
leafTexts: [
|
|
62
|
-
'合集',
|
|
63
|
-
'03:55',
|
|
64
|
-
'1.9万',
|
|
65
|
-
'Python邪修,5分钟学完Python基础 #python #编程',
|
|
66
|
-
'@',
|
|
67
|
-
'校长讲python(无小号)',
|
|
68
|
-
'5月前',
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
const rows = await cmd.func(page, { query: 'python', limit: 5 });
|
|
75
|
-
expect(page.goto).toHaveBeenCalledWith('https://www.douyin.com/search/python?type=video');
|
|
76
|
-
expect(rows).toEqual([
|
|
77
|
-
{
|
|
78
|
-
rank: 1,
|
|
79
|
-
desc: 'Python邪修,5分钟学完Python基础 #python #编程',
|
|
80
|
-
author: '校长讲python(无小号)',
|
|
81
|
-
url: 'https://www.douyin.com/video/7585120459717365001',
|
|
82
|
-
plays: 0,
|
|
83
|
-
likes: 19000,
|
|
84
|
-
comments: 0,
|
|
85
|
-
shares: 0,
|
|
86
|
-
},
|
|
87
|
-
]);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('encodes Chinese keywords in the URL path', async () => {
|
|
91
|
-
const cmd = getRegistry().get('douyin/search');
|
|
92
|
-
const page = createPageMock({ evaluateResult: { state: 'rendered', cards: [{ url: '/video/1', leafTexts: ['hi'] }] } });
|
|
93
|
-
await cmd.func(page, { query: 'AI 编程', limit: 1 });
|
|
94
|
-
expect(page.goto).toHaveBeenCalledWith('https://www.douyin.com/search/AI%20%E7%BC%96%E7%A8%8B?type=video');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('respects --limit cap when the page rendered more cards than requested', async () => {
|
|
98
|
-
const cmd = getRegistry().get('douyin/search');
|
|
99
|
-
const cards = Array.from({ length: 12 }, (_, i) => ({
|
|
100
|
-
url: `//www.douyin.com/video/100000${i}`,
|
|
101
|
-
leafTexts: ['03:00', `${i + 1}万`, `video ${i}`, '@', `user${i}`],
|
|
102
|
-
}));
|
|
103
|
-
const page = createPageMock({ evaluateResult: { state: 'rendered', cards } });
|
|
104
|
-
const rows = await cmd.func(page, { query: 'x', limit: 3 });
|
|
105
|
-
expect(rows).toHaveLength(3);
|
|
106
|
-
expect(rows.map((r) => r.url)).toEqual([
|
|
107
|
-
'https://www.douyin.com/video/1000000',
|
|
108
|
-
'https://www.douyin.com/video/1000001',
|
|
109
|
-
'https://www.douyin.com/video/1000002',
|
|
110
|
-
]);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('maps the explicit login-wall state to AuthRequiredError', async () => {
|
|
114
|
-
const cmd = getRegistry().get('douyin/search');
|
|
115
|
-
const page = createPageMock({ evaluateResult: { state: 'login_wall' } });
|
|
116
|
-
await expect(cmd.func(page, { query: 'x', limit: 1 })).rejects.toMatchObject({
|
|
117
|
-
code: 'AUTH_REQUIRED',
|
|
118
|
-
message: expect.stringContaining('login wall'),
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('maps explicit empty search state to EmptyResultError', async () => {
|
|
123
|
-
const cmd = getRegistry().get('douyin/search');
|
|
124
|
-
const page = createPageMock({ evaluateResult: { state: 'empty' } });
|
|
125
|
-
await expect(cmd.func(page, { query: 'x', limit: 1 })).rejects.toMatchObject({
|
|
126
|
-
code: 'EMPTY_RESULT',
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('maps timeout state to CommandExecutionError instead of treating parser drift as auth or empty', async () => {
|
|
131
|
-
const cmd = getRegistry().get('douyin/search');
|
|
132
|
-
const page = createPageMock({ evaluateResult: { state: 'timeout' } });
|
|
133
|
-
await expect(cmd.func(page, { query: 'x', limit: 1 })).rejects.toMatchObject({
|
|
134
|
-
code: 'COMMAND_EXEC',
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('unwraps Browser Bridge {session, data} envelopes before inspecting state', async () => {
|
|
139
|
-
const cmd = getRegistry().get('douyin/search');
|
|
140
|
-
const page = createPageMock({
|
|
141
|
-
evaluateResult: {
|
|
142
|
-
session: 'site:douyin',
|
|
143
|
-
data: { state: 'rendered', cards: [{ url: '/video/9', leafTexts: ['demo'] }] },
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
const rows = await cmd.func(page, { query: 'x', limit: 1 });
|
|
147
|
-
expect(rows).toHaveLength(1);
|
|
148
|
-
expect(rows[0].url).toBe('https://www.douyin.com/video/9');
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('throws CommandExecutionError on malformed evaluator payload', async () => {
|
|
152
|
-
const cmd = getRegistry().get('douyin/search');
|
|
153
|
-
const page = createPageMock({ evaluateResult: 'not-an-object' });
|
|
154
|
-
await expect(cmd.func(page, { query: 'x', limit: 1 })).rejects.toMatchObject({
|
|
155
|
-
code: 'COMMAND_EXEC',
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('throws CommandExecutionError on malformed cards payload', async () => {
|
|
160
|
-
const cmd = getRegistry().get('douyin/search');
|
|
161
|
-
const page = createPageMock({ evaluateResult: { state: 'rendered', cards: { bad: true } } });
|
|
162
|
-
await expect(cmd.func(page, { query: 'x', limit: 1 })).rejects.toMatchObject({
|
|
163
|
-
code: 'COMMAND_EXEC',
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('fails closed instead of partially returning cards missing stable url or desc', async () => {
|
|
168
|
-
const cmd = getRegistry().get('douyin/search');
|
|
169
|
-
const page = createPageMock({
|
|
170
|
-
evaluateResult: {
|
|
171
|
-
state: 'rendered',
|
|
172
|
-
cards: [
|
|
173
|
-
{ url: '/video/123', leafTexts: ['03:00', 'valid desc'] },
|
|
174
|
-
{ url: 'https://evil.test/video/456', leafTexts: ['03:00', 'invalid url'] },
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
await expect(cmd.func(page, { query: 'x', limit: 2 })).rejects.toMatchObject({
|
|
179
|
-
code: 'COMMAND_EXEC',
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('fails closed when a card only has metadata text and no stable desc', async () => {
|
|
184
|
-
const cmd = getRegistry().get('douyin/search');
|
|
185
|
-
const page = createPageMock({
|
|
186
|
-
evaluateResult: {
|
|
187
|
-
state: 'rendered',
|
|
188
|
-
cards: [
|
|
189
|
-
{ url: '/video/123', leafTexts: ['合集', '03:00', '1.2万', '@', '作者名', '5月前'] },
|
|
190
|
-
],
|
|
191
|
-
},
|
|
192
|
-
});
|
|
193
|
-
await expect(cmd.func(page, { query: 'x', limit: 1 })).rejects.toMatchObject({
|
|
194
|
-
code: 'COMMAND_EXEC',
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe('parseDouyinCount', () => {
|
|
200
|
-
it.each([
|
|
201
|
-
['1.9万', 19_000],
|
|
202
|
-
['3万', 30_000],
|
|
203
|
-
['4702', 4702],
|
|
204
|
-
['1,234', 1234],
|
|
205
|
-
['1.2亿', 120_000_000],
|
|
206
|
-
['', 0],
|
|
207
|
-
['unknown', 0],
|
|
208
|
-
[null, 0],
|
|
209
|
-
[undefined, 0],
|
|
210
|
-
])('parses %j as %i', (input, expected) => {
|
|
211
|
-
expect(parseDouyinCount(input)).toBe(expected);
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('normalizeDouyinVideoUrl', () => {
|
|
216
|
-
it.each([
|
|
217
|
-
['//www.douyin.com/video/123', 'https://www.douyin.com/video/123'],
|
|
218
|
-
['/video/123?foo=bar', 'https://www.douyin.com/video/123'],
|
|
219
|
-
['https://www.douyin.com/video/123?something', 'https://www.douyin.com/video/123'],
|
|
220
|
-
['https://evil.test/video/123', ''],
|
|
221
|
-
['https://www.douyin.com/user/video/123', ''],
|
|
222
|
-
['', ''],
|
|
223
|
-
[null, ''],
|
|
224
|
-
])('normalizes %j → %j', (input, expected) => {
|
|
225
|
-
expect(normalizeDouyinVideoUrl(input)).toBe(expected);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('extracts only stable Douyin video ids', () => {
|
|
229
|
-
expect(extractDouyinVideoId('https://www.douyin.com/video/123')).toBe('123');
|
|
230
|
-
expect(extractDouyinVideoId('//www.douyin.com/video/456')).toBe('456');
|
|
231
|
-
expect(extractDouyinVideoId('https://evil.test/video/123')).toBe('');
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe('projectCard', () => {
|
|
236
|
-
it('extracts duration/likes/desc/author by leaf-text shape, classname-agnostic', () => {
|
|
237
|
-
const row = projectCard({
|
|
238
|
-
url: '//www.douyin.com/video/7585120459717365001',
|
|
239
|
-
leafTexts: ['合集', '03:55', '1.9万', 'Python邪修', '@', '校长', '5月前'],
|
|
240
|
-
}, 0);
|
|
241
|
-
expect(row).toEqual({
|
|
242
|
-
rank: 1,
|
|
243
|
-
desc: 'Python邪修',
|
|
244
|
-
author: '校长',
|
|
245
|
-
url: 'https://www.douyin.com/video/7585120459717365001',
|
|
246
|
-
plays: 0,
|
|
247
|
-
likes: 19000,
|
|
248
|
-
comments: 0,
|
|
249
|
-
shares: 0,
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('returns the longest non-skipped text as desc, not the publish-date suffix', () => {
|
|
254
|
-
const row = projectCard({
|
|
255
|
-
url: '/video/1',
|
|
256
|
-
leafTexts: ['02:00', '4702', 'hi long-text', '@', 'user', '1月前'],
|
|
257
|
-
}, 0);
|
|
258
|
-
expect(row.desc).toBe('hi long-text');
|
|
259
|
-
expect(row.author).toBe('user');
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('strips a fused @author prefix from the desc when present', () => {
|
|
263
|
-
const row = projectCard({
|
|
264
|
-
url: '/video/1',
|
|
265
|
-
leafTexts: ['02:00', '100', '@alice this is the caption', '@', 'alice'],
|
|
266
|
-
}, 0);
|
|
267
|
-
expect(row.author).toBe('alice');
|
|
268
|
-
expect(row.desc).toBe('this is the caption');
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('returns safe defaults when leafTexts is missing', () => {
|
|
272
|
-
const row = projectCard({ url: '/video/42', leafTexts: undefined }, 4);
|
|
273
|
-
expect(row).toEqual({
|
|
274
|
-
rank: 5,
|
|
275
|
-
desc: '',
|
|
276
|
-
author: '',
|
|
277
|
-
url: 'https://www.douyin.com/video/42',
|
|
278
|
-
plays: 0,
|
|
279
|
-
likes: 0,
|
|
280
|
-
comments: 0,
|
|
281
|
-
shares: 0,
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('returns rank=index+1 regardless of input', () => {
|
|
286
|
-
const row = projectCard({ url: '/video/1', leafTexts: ['x'] }, 9);
|
|
287
|
-
expect(row.rank).toBe(10);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it('projects cards and reports malformed rows in the returned window', () => {
|
|
291
|
-
const result = projectSearchCards([
|
|
292
|
-
{ url: '/video/1', leafTexts: ['caption'] },
|
|
293
|
-
{ url: '/video/not-numeric', leafTexts: ['bad'] },
|
|
294
|
-
{ url: '/video/3', leafTexts: [] },
|
|
295
|
-
], 3);
|
|
296
|
-
expect(result.rows).toHaveLength(1);
|
|
297
|
-
expect(result.invalidCount).toBe(2);
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
it('does not treat metadata-only leaf text as a stable desc', () => {
|
|
301
|
-
const result = projectSearchCards([
|
|
302
|
-
{ url: '/video/1', leafTexts: ['合集', '03:55', '1.9万', '@', '校长', '5月前'] },
|
|
303
|
-
], 1);
|
|
304
|
-
expect(result.rows).toHaveLength(0);
|
|
305
|
-
expect(result.invalidCount).toBe(1);
|
|
306
|
-
});
|
|
307
|
-
});
|
package/clis/douyin/stats.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@agentrhq/webcmd/registry';
|
|
2
|
-
import { browserFetch } from './_shared/browser-fetch.js';
|
|
3
|
-
cli({
|
|
4
|
-
site: 'douyin',
|
|
5
|
-
name: 'stats',
|
|
6
|
-
access: 'read',
|
|
7
|
-
description: '作品数据分析',
|
|
8
|
-
domain: 'creator.douyin.com',
|
|
9
|
-
strategy: Strategy.COOKIE,
|
|
10
|
-
args: [
|
|
11
|
-
{ name: 'aweme_id', required: true, positional: true, help: '抖音作品 ID(aweme_id,可从作品 URL 末尾获取)' },
|
|
12
|
-
],
|
|
13
|
-
columns: ['metric', 'value'],
|
|
14
|
-
func: async (page, kwargs) => {
|
|
15
|
-
const now = Math.floor(Date.now() / 1000);
|
|
16
|
-
const sevenDaysAgo = now - 7 * 86400;
|
|
17
|
-
const url = 'https://creator.douyin.com/janus/douyin/creator/data/item_analysis/metrics_trend';
|
|
18
|
-
const body = {
|
|
19
|
-
aweme_id: kwargs.aweme_id,
|
|
20
|
-
start_time: sevenDaysAgo,
|
|
21
|
-
end_time: now,
|
|
22
|
-
metrics: ['play_count', 'like_count', 'comment_count', 'share_count'],
|
|
23
|
-
};
|
|
24
|
-
const res = await browserFetch(page, 'POST', url, { body });
|
|
25
|
-
const data = res.data ?? {};
|
|
26
|
-
return Object.entries(data).map(([metric, value]) => ({ metric, value }));
|
|
27
|
-
},
|
|
28
|
-
});
|