@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,126 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { getRegistry } from '@agentrhq/webcmd/registry';
|
|
3
|
-
import './feed.js';
|
|
4
|
-
import { buildFeedNoteUrl } from './feed.js';
|
|
5
|
-
|
|
6
|
-
const HOST = 'www.xiaohongshu.com';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Minimal page mock: feed reads the store via a single page.evaluate call.
|
|
10
|
-
* `evaluateResult` is what that evaluate resolves to (the store-read envelope).
|
|
11
|
-
*/
|
|
12
|
-
function createPageMock(evaluateResult) {
|
|
13
|
-
return {
|
|
14
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
15
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
16
|
-
evaluate: vi.fn().mockResolvedValue(evaluateResult),
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function entry(id, overrides = {}) {
|
|
21
|
-
return {
|
|
22
|
-
id,
|
|
23
|
-
title: `title-${id}`,
|
|
24
|
-
type: 'normal',
|
|
25
|
-
author: `author-${id}`,
|
|
26
|
-
likes: '100',
|
|
27
|
-
xsecToken: `tok-${id}`,
|
|
28
|
-
...overrides,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe('xiaohongshu/feed buildFeedNoteUrl', () => {
|
|
33
|
-
it('appends xsec_token and an empty xsec_source when a token is present', () => {
|
|
34
|
-
expect(buildFeedNoteUrl(HOST, 'abc123', 'TOK')).toBe(
|
|
35
|
-
`https://${HOST}/explore/abc123?xsec_token=TOK&xsec_source=`,
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('falls back to a bare /explore URL when the token is empty', () => {
|
|
40
|
-
expect(buildFeedNoteUrl(HOST, 'abc123', '')).toBe(`https://${HOST}/explore/abc123`);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('URL-encodes xsec_token instead of interpolating raw query text', () => {
|
|
44
|
-
expect(buildFeedNoteUrl(HOST, 'abc123', 'a&b=c d')).toBe(
|
|
45
|
-
`https://${HOST}/explore/abc123?xsec_token=a%26b%3Dc+d&xsec_source=`,
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('xiaohongshu/feed func', () => {
|
|
51
|
-
const feed = getRegistry().get('xiaohongshu/feed');
|
|
52
|
-
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
expect(feed).toBeDefined();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('emits signed note URLs from the hydrated store', async () => {
|
|
58
|
-
const page = createPageMock({ items: [entry('id1'), entry('id2')] });
|
|
59
|
-
const rows = await feed.func(page, { limit: 20 });
|
|
60
|
-
expect(rows).toEqual([
|
|
61
|
-
{ id: 'id1', title: 'title-id1', type: 'normal', author: 'author-id1', likes: '100', url: `https://${HOST}/explore/id1?xsec_token=tok-id1&xsec_source=` },
|
|
62
|
-
{ id: 'id2', title: 'title-id2', type: 'normal', author: 'author-id2', likes: '100', url: `https://${HOST}/explore/id2?xsec_token=tok-id2&xsec_source=` },
|
|
63
|
-
]);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('unwraps Browser Bridge evaluate envelopes', async () => {
|
|
67
|
-
const page = createPageMock({ session: { id: 's1' }, data: { items: [entry('id1')] } });
|
|
68
|
-
const rows = await feed.func(page, { limit: 20 });
|
|
69
|
-
expect(rows[0].url).toBe(`https://${HOST}/explore/id1?xsec_token=tok-id1&xsec_source=`);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('typed-fails when a feed entry is missing its note token', async () => {
|
|
73
|
-
const page = createPageMock({ items: [entry('id1', { xsecToken: '' })] });
|
|
74
|
-
await expect(feed.func(page, { limit: 20 })).rejects.toMatchObject({
|
|
75
|
-
code: 'COMMAND_EXEC',
|
|
76
|
-
message: expect.stringContaining('xsecToken'),
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('truncates to --limit', async () => {
|
|
81
|
-
const page = createPageMock({ items: [entry('a'), entry('b'), entry('c')] });
|
|
82
|
-
const rows = await feed.func(page, { limit: 2 });
|
|
83
|
-
expect(rows.map((r) => r.id)).toEqual(['a', 'b']);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('typed-fails when a feed entry is missing its note id', async () => {
|
|
87
|
-
const page = createPageMock({ items: [entry(''), entry('keep')] });
|
|
88
|
-
await expect(feed.func(page, { limit: 20 })).rejects.toMatchObject({
|
|
89
|
-
code: 'COMMAND_EXEC',
|
|
90
|
-
message: expect.stringContaining('note id'),
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('rejects invalid --limit before browser navigation', async () => {
|
|
95
|
-
const page = createPageMock({ items: [] });
|
|
96
|
-
await expect(feed.func(page, { limit: 0 })).rejects.toMatchObject({ code: 'ARGUMENT' });
|
|
97
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('maps a store read failure to CommandExecutionError', async () => {
|
|
101
|
-
const page = createPageMock({ error: 'no_pinia' });
|
|
102
|
-
await expect(feed.func(page, { limit: 20 })).rejects.toMatchObject({
|
|
103
|
-
code: 'COMMAND_EXEC',
|
|
104
|
-
message: expect.stringContaining('no_pinia'),
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('maps an empty store to EmptyResultError', async () => {
|
|
109
|
-
const page = createPageMock({ items: [] });
|
|
110
|
-
await expect(feed.func(page, { limit: 20 })).rejects.toMatchObject({ code: 'EMPTY_RESULT' });
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('maps a malformed items payload to CommandExecutionError', async () => {
|
|
114
|
-
const page = createPageMock({ items: {} });
|
|
115
|
-
await expect(feed.func(page, { limit: 20 })).rejects.toMatchObject({ code: 'COMMAND_EXEC' });
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('reads the note token from the entry top level, not card.user', async () => {
|
|
119
|
-
// entry.xsecToken is the note's token; card.user.xsecToken is the
|
|
120
|
-
// author profile's — the func must not confuse them.
|
|
121
|
-
const item = entry('id1', { xsecToken: 'note-token' });
|
|
122
|
-
const page = createPageMock({ items: [item] });
|
|
123
|
-
const rows = await feed.func(page, { limit: 20 });
|
|
124
|
-
expect(rows[0].url).toContain('xsec_token=note-token');
|
|
125
|
-
});
|
|
126
|
-
});
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Xiaohongshu follow — clicks the follow button on a user's profile page.
|
|
3
|
-
*
|
|
4
|
-
* xhs public web APIs require `x-s`/`x-t`/`x-s-common` signing that the page
|
|
5
|
-
* context can produce but cannot be replayed reliably from outside, so we drive
|
|
6
|
-
* the UI instead (same approach as delete-note.js / publish.js).
|
|
7
|
-
*
|
|
8
|
-
* Flow:
|
|
9
|
-
* 1. Navigate to https://www.xiaohongshu.com/user/profile/<userId>
|
|
10
|
-
* 2. Detect login redirect (xhs bounces to /login on auth failure)
|
|
11
|
-
* 3. Walk visible <button>/[role=button] elements, restricted to profile-header
|
|
12
|
-
* containers when present, and text-match against follow / following labels
|
|
13
|
-
* 4. If already following → return 'already-following' without clicking
|
|
14
|
-
* 5. Otherwise click and poll button text for the flip to 已关注 within 5s
|
|
15
|
-
*
|
|
16
|
-
* Requires: logged into www.xiaohongshu.com in Chrome.
|
|
17
|
-
*/
|
|
18
|
-
import { cli, Strategy } from '@agentrhq/webcmd/registry';
|
|
19
|
-
import { ArgumentError, AuthRequiredError, CliError, CommandExecutionError } from '@agentrhq/webcmd/errors';
|
|
20
|
-
import { normalizeXhsUserId } from './user-helpers.js';
|
|
21
|
-
|
|
22
|
-
const PROFILE_SETTLE_MS = 2500;
|
|
23
|
-
const STATE_FLIP_TIMEOUT_MS = 5000;
|
|
24
|
-
const USER_ID_RE = /^[a-zA-Z0-9]{8,32}$/;
|
|
25
|
-
|
|
26
|
-
function isXiaohongshuHost(hostname) {
|
|
27
|
-
const host = String(hostname || '').toLowerCase();
|
|
28
|
-
return host === 'xiaohongshu.com' || host.endsWith('.xiaohongshu.com');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function unwrapEvaluateResult(payload) {
|
|
32
|
-
if (payload && typeof payload === 'object' && 'session' in payload && 'data' in payload) {
|
|
33
|
-
return payload.data;
|
|
34
|
-
}
|
|
35
|
-
return payload;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function requireActionResult(payload, context) {
|
|
39
|
-
const inner = unwrapEvaluateResult(payload);
|
|
40
|
-
if (!inner || typeof inner !== 'object' || Array.isArray(inner) || typeof inner.ok !== 'boolean') {
|
|
41
|
-
throw new CommandExecutionError(`xiaohongshu/follow: malformed ${context} payload`);
|
|
42
|
-
}
|
|
43
|
-
return inner;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function assertUserId(raw) {
|
|
47
|
-
const input = String(raw ?? '').trim();
|
|
48
|
-
if (/^https?:\/\//i.test(input)) {
|
|
49
|
-
let parsed;
|
|
50
|
-
try {
|
|
51
|
-
parsed = new URL(input);
|
|
52
|
-
} catch {
|
|
53
|
-
throw new ArgumentError('xiaohongshu/follow: invalid profile URL');
|
|
54
|
-
}
|
|
55
|
-
if (parsed.protocol !== 'https:' || !isXiaohongshuHost(parsed.hostname)) {
|
|
56
|
-
throw new ArgumentError('xiaohongshu/follow: profile URL must be an exact https://*.xiaohongshu.com URL');
|
|
57
|
-
}
|
|
58
|
-
const match = parsed.pathname.match(/^\/user\/profile\/([a-zA-Z0-9]{8,32})\/?$/);
|
|
59
|
-
if (!match) {
|
|
60
|
-
throw new ArgumentError('xiaohongshu/follow: profile URL must be /user/profile/<userId>');
|
|
61
|
-
}
|
|
62
|
-
return match[1];
|
|
63
|
-
}
|
|
64
|
-
const userId = normalizeXhsUserId(raw);
|
|
65
|
-
if (!userId || !USER_ID_RE.test(userId)) {
|
|
66
|
-
throw new ArgumentError(
|
|
67
|
-
'xiaohongshu/follow: user-id must be a Xiaohongshu user ID (e.g. 5d8f88dc0000000001005d3a) or full profile URL',
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
return userId;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The injected page script. Lives in the browser context, so it can't import
|
|
75
|
-
* anything — every helper is inlined. Returns `{ ok, state, reason? }` where
|
|
76
|
-
* `state` is one of: 'followed' | 'already-following' | 'failed'.
|
|
77
|
-
*/
|
|
78
|
-
function buildFollowScript() {
|
|
79
|
-
return `
|
|
80
|
-
(async () => {
|
|
81
|
-
const FOLLOW_LABELS = ['关注', '+ 关注', '+关注'];
|
|
82
|
-
const FOLLOWING_LABELS = ['已关注', '已互关', '互相关注'];
|
|
83
|
-
const STATE_FLIP_TIMEOUT_MS = ${STATE_FLIP_TIMEOUT_MS};
|
|
84
|
-
const STATE_POLL_MS = 250;
|
|
85
|
-
|
|
86
|
-
const isVisible = (el) => !!el && el.offsetParent !== null;
|
|
87
|
-
const textOf = (el) => (el.innerText || el.textContent || '').trim();
|
|
88
|
-
|
|
89
|
-
// Restrict the search to plausible profile-header containers when any are
|
|
90
|
-
// present. This stops the walker from picking up the "关注" tab label
|
|
91
|
-
// (rendered as a div / span in the user-detail timeline tabs) which would
|
|
92
|
-
// misclick into the tab nav instead of the follow CTA. If none of the
|
|
93
|
-
// containers match the page layout we fall back to a global button sweep.
|
|
94
|
-
const SCOPE_SELECTORS = [
|
|
95
|
-
'.user-info', '.profile-info', '.user-detail', '.profile-page',
|
|
96
|
-
'[class*="user-info"]', '[class*="profile"]',
|
|
97
|
-
];
|
|
98
|
-
const scopes = SCOPE_SELECTORS.flatMap((sel) => Array.from(document.querySelectorAll(sel)));
|
|
99
|
-
const visibleScopes = scopes.filter(isVisible);
|
|
100
|
-
const roots = visibleScopes.length > 0 ? visibleScopes : [document];
|
|
101
|
-
|
|
102
|
-
// Collect candidate buttons within scopes. Only real buttons and ARIA
|
|
103
|
-
// buttons are considered — text nodes inside divs (tab labels) are skipped.
|
|
104
|
-
const collectButtons = () => {
|
|
105
|
-
const out = new Set();
|
|
106
|
-
for (const root of roots) {
|
|
107
|
-
const els = root.querySelectorAll('button, [role="button"]');
|
|
108
|
-
for (const el of els) {
|
|
109
|
-
if (isVisible(el)) out.add(el);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return Array.from(out);
|
|
113
|
-
};
|
|
114
|
-
const findButtonByLabels = (labels) => {
|
|
115
|
-
for (const btn of collectButtons()) {
|
|
116
|
-
const t = textOf(btn);
|
|
117
|
-
if (labels.includes(t)) return btn;
|
|
118
|
-
}
|
|
119
|
-
return null;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
// Idempotent fast path: viewer already follows the target.
|
|
123
|
-
if (findButtonByLabels(FOLLOWING_LABELS)) {
|
|
124
|
-
return { ok: true, state: 'already-following' };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const followBtn = findButtonByLabels(FOLLOW_LABELS);
|
|
128
|
-
if (!followBtn) {
|
|
129
|
-
return {
|
|
130
|
-
ok: false,
|
|
131
|
-
state: 'failed',
|
|
132
|
-
reason: 'Follow button not found on profile (logged out, blocked, or selectors changed). Re-run after login or update follow.js label/scope list.',
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
followBtn.click();
|
|
136
|
-
|
|
137
|
-
// Verify the button text flipped to 已关注 / 已互关 within the timeout.
|
|
138
|
-
// Polling rather than mutation-observing keeps the script resilient to
|
|
139
|
-
// xhs's React re-renders that swap the underlying DOM node.
|
|
140
|
-
const deadline = Date.now() + STATE_FLIP_TIMEOUT_MS;
|
|
141
|
-
while (Date.now() < deadline) {
|
|
142
|
-
await new Promise((r) => setTimeout(r, STATE_POLL_MS));
|
|
143
|
-
if (findButtonByLabels(FOLLOWING_LABELS)) {
|
|
144
|
-
return { ok: true, state: 'followed' };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
ok: false,
|
|
149
|
-
state: 'failed',
|
|
150
|
-
reason: 'Follow button click did not flip to 已关注 within ' + (STATE_FLIP_TIMEOUT_MS / 1000) + 's',
|
|
151
|
-
};
|
|
152
|
-
})()
|
|
153
|
-
`;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
cli({
|
|
157
|
-
site: 'xiaohongshu',
|
|
158
|
-
name: 'follow',
|
|
159
|
-
access: 'write',
|
|
160
|
-
description: '关注小红书用户 (profile UI automation)',
|
|
161
|
-
domain: 'www.xiaohongshu.com',
|
|
162
|
-
strategy: Strategy.COOKIE,
|
|
163
|
-
navigateBefore: false,
|
|
164
|
-
browser: true,
|
|
165
|
-
args: [
|
|
166
|
-
{
|
|
167
|
-
name: 'user-id',
|
|
168
|
-
required: true,
|
|
169
|
-
positional: true,
|
|
170
|
-
help: 'User ID (e.g. 5d8f88dc0000000001005d3a) or profile URL',
|
|
171
|
-
},
|
|
172
|
-
],
|
|
173
|
-
columns: ['status', 'user_id', 'url'],
|
|
174
|
-
func: async (page, kwargs) => {
|
|
175
|
-
if (!page) {
|
|
176
|
-
throw new CommandExecutionError('Browser session required for xiaohongshu follow');
|
|
177
|
-
}
|
|
178
|
-
try {
|
|
179
|
-
const userId = assertUserId(kwargs['user-id']);
|
|
180
|
-
const url = `https://www.xiaohongshu.com/user/profile/${userId}`;
|
|
181
|
-
await page.goto(url);
|
|
182
|
-
await page.wait({ time: PROFILE_SETTLE_MS / 1000 });
|
|
183
|
-
|
|
184
|
-
const hrefRaw = unwrapEvaluateResult(await page.evaluate('() => location.href'));
|
|
185
|
-
if (typeof hrefRaw !== 'string') {
|
|
186
|
-
throw new CommandExecutionError('xiaohongshu/follow: malformed current-url payload');
|
|
187
|
-
}
|
|
188
|
-
const parsedHref = new URL(hrefRaw);
|
|
189
|
-
if (parsedHref.protocol !== 'https:' || !isXiaohongshuHost(parsedHref.hostname)) {
|
|
190
|
-
throw new CommandExecutionError(
|
|
191
|
-
`xiaohongshu/follow: expected Xiaohongshu profile host, got ${parsedHref.hostname}`,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
if (/\/login(?:[/?#]|$)/i.test(parsedHref.pathname)) {
|
|
195
|
-
throw new AuthRequiredError('www.xiaohongshu.com');
|
|
196
|
-
}
|
|
197
|
-
const currentProfile = parsedHref.pathname.match(/^\/user\/profile\/([a-zA-Z0-9]{8,32})\/?$/);
|
|
198
|
-
if (currentProfile?.[1] !== userId) {
|
|
199
|
-
throw new CommandExecutionError(
|
|
200
|
-
`xiaohongshu/follow: expected profile ${userId}, got ${parsedHref.pathname}`,
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const result = requireActionResult(
|
|
205
|
-
await page.evaluate(buildFollowScript()),
|
|
206
|
-
'follow-action',
|
|
207
|
-
);
|
|
208
|
-
if (!result.ok) {
|
|
209
|
-
throw new CommandExecutionError(
|
|
210
|
-
`xiaohongshu/follow failed: ${result.reason ?? 'unknown reason'}`,
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
return [{ status: result.state, user_id: userId, url }];
|
|
214
|
-
} catch (err) {
|
|
215
|
-
if (err instanceof CliError) throw err;
|
|
216
|
-
throw new CommandExecutionError(
|
|
217
|
-
`xiaohongshu/follow failed: ${err?.message ?? String(err)}`,
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
export const __test__ = {
|
|
224
|
-
assertUserId,
|
|
225
|
-
buildFollowScript,
|
|
226
|
-
};
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { getRegistry } from '@agentrhq/webcmd/registry';
|
|
3
|
-
import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@agentrhq/webcmd/errors';
|
|
4
|
-
|
|
5
|
-
import { __test__ } from './follow.js';
|
|
6
|
-
import './unfollow.js';
|
|
7
|
-
|
|
8
|
-
function makePage(evaluateResults = []) {
|
|
9
|
-
const evaluate = vi.fn();
|
|
10
|
-
for (const r of evaluateResults) evaluate.mockResolvedValueOnce(r);
|
|
11
|
-
evaluate.mockResolvedValue(undefined);
|
|
12
|
-
return {
|
|
13
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
14
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
15
|
-
evaluate,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe('xiaohongshu follow', () => {
|
|
20
|
-
const getCommand = () => getRegistry().get('xiaohongshu/follow');
|
|
21
|
-
const validId = '5d8f88dc0000000001005d3a';
|
|
22
|
-
|
|
23
|
-
it('returns followed when the follow click and state flip both succeed', async () => {
|
|
24
|
-
const page = makePage([
|
|
25
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
26
|
-
{ ok: true, state: 'followed' },
|
|
27
|
-
]);
|
|
28
|
-
const result = await getCommand().func(page, { 'user-id': validId });
|
|
29
|
-
expect(result).toEqual([{
|
|
30
|
-
status: 'followed',
|
|
31
|
-
user_id: validId,
|
|
32
|
-
url: `https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
33
|
-
}]);
|
|
34
|
-
expect(page.goto).toHaveBeenCalledWith(`https://www.xiaohongshu.com/user/profile/${validId}`);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('returns already-following when the profile shows 已关注 on entry', async () => {
|
|
38
|
-
const page = makePage([
|
|
39
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
40
|
-
{ ok: true, state: 'already-following' },
|
|
41
|
-
]);
|
|
42
|
-
const result = await getCommand().func(page, { 'user-id': validId });
|
|
43
|
-
expect(result[0].status).toBe('already-following');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('accepts a full profile URL and extracts the user id', async () => {
|
|
47
|
-
const page = makePage([
|
|
48
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
49
|
-
{ ok: true, state: 'followed' },
|
|
50
|
-
]);
|
|
51
|
-
await getCommand().func(page, {
|
|
52
|
-
'user-id': `https://www.xiaohongshu.com/user/profile/${validId}?xsec_token=abc&xsec_source=pc`,
|
|
53
|
-
});
|
|
54
|
-
expect(page.goto).toHaveBeenCalledWith(`https://www.xiaohongshu.com/user/profile/${validId}`);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('unwraps browser bridge envelopes at every evaluate boundary', async () => {
|
|
58
|
-
const page = makePage([
|
|
59
|
-
{ session: 's', data: `https://www.xiaohongshu.com/user/profile/${validId}` },
|
|
60
|
-
{ session: 's', data: { ok: true, state: 'followed' } },
|
|
61
|
-
]);
|
|
62
|
-
const result = await getCommand().func(page, { 'user-id': validId });
|
|
63
|
-
expect(result[0].status).toBe('followed');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('rejects malformed user ids before navigation', async () => {
|
|
67
|
-
const page = makePage();
|
|
68
|
-
await expect(getCommand().func(page, { 'user-id': '' })).rejects.toBeInstanceOf(ArgumentError);
|
|
69
|
-
await expect(getCommand().func(page, { 'user-id': 'short' })).rejects.toBeInstanceOf(ArgumentError);
|
|
70
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('throws AuthRequiredError when xhs redirects to /login', async () => {
|
|
74
|
-
const page = makePage([
|
|
75
|
-
'https://www.xiaohongshu.com/login?redirectPath=/user/profile/' + validId,
|
|
76
|
-
]);
|
|
77
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toBeInstanceOf(AuthRequiredError);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('throws CommandExecutionError when navigation lands on a different profile', async () => {
|
|
81
|
-
const page = makePage([
|
|
82
|
-
'https://www.xiaohongshu.com/user/profile/5d8f88dc0000000001005d4b',
|
|
83
|
-
]);
|
|
84
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toThrowError(/expected profile/);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('throws CommandExecutionError when the follow button is not found', async () => {
|
|
88
|
-
const page = makePage([
|
|
89
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
90
|
-
{ ok: false, state: 'failed', reason: 'Follow button not found on profile' },
|
|
91
|
-
]);
|
|
92
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toThrowError(/Follow button not found/);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('throws CommandExecutionError when the state flip times out', async () => {
|
|
96
|
-
const page = makePage([
|
|
97
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
98
|
-
{ ok: false, state: 'failed', reason: 'Follow button click did not flip to 已关注 within 5s' },
|
|
99
|
-
]);
|
|
100
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toBeInstanceOf(CommandExecutionError);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('throws CommandExecutionError for malformed evaluate payloads', async () => {
|
|
104
|
-
const page1 = makePage([
|
|
105
|
-
{ not: 'a string' },
|
|
106
|
-
]);
|
|
107
|
-
await expect(getCommand().func(page1, { 'user-id': validId })).rejects.toThrowError(/malformed current-url payload/);
|
|
108
|
-
|
|
109
|
-
const page2 = makePage([
|
|
110
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
111
|
-
{ ok: 'yes', state: 'followed' },
|
|
112
|
-
]);
|
|
113
|
-
await expect(getCommand().func(page2, { 'user-id': validId })).rejects.toThrowError(/malformed follow-action payload/);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('__test__.assertUserId', () => {
|
|
117
|
-
it('normalizes raw user ids and URL forms', () => {
|
|
118
|
-
expect(__test__.assertUserId(validId)).toBe(validId);
|
|
119
|
-
expect(__test__.assertUserId(`https://www.xiaohongshu.com/user/profile/${validId}`)).toBe(validId);
|
|
120
|
-
expect(__test__.assertUserId(`https://www.xiaohongshu.com/user/profile/${validId}?xsec_token=t`)).toBe(validId);
|
|
121
|
-
expect(__test__.assertUserId(`https://www.xiaohongshu.com/user/profile/${validId}/`)).toBe(validId);
|
|
122
|
-
});
|
|
123
|
-
it('rejects too-short or non-alphanumeric ids', () => {
|
|
124
|
-
expect(() => __test__.assertUserId('')).toThrow(ArgumentError);
|
|
125
|
-
expect(() => __test__.assertUserId('abc')).toThrow(ArgumentError);
|
|
126
|
-
expect(() => __test__.assertUserId('!!!')).toThrow(ArgumentError);
|
|
127
|
-
expect(() => __test__.assertUserId(`https://evil.example/user/profile/${validId}`)).toThrow(ArgumentError);
|
|
128
|
-
expect(() => __test__.assertUserId(`https://www.xiaohongshu.com/user/profile/${validId}/note123`)).toThrow(ArgumentError);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('throws CommandExecutionError when navigation lands on a non-Xiaohongshu host', async () => {
|
|
133
|
-
const page = makePage([
|
|
134
|
-
`https://evil.example/user/profile/${validId}`,
|
|
135
|
-
]);
|
|
136
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toThrowError(/expected Xiaohongshu profile host/);
|
|
137
|
-
expect(page.evaluate).toHaveBeenCalledTimes(1);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('xiaohongshu unfollow', () => {
|
|
142
|
-
const getCommand = () => getRegistry().get('xiaohongshu/unfollow');
|
|
143
|
-
const validId = '5d8f88dc0000000001005d3a';
|
|
144
|
-
|
|
145
|
-
it('returns unfollowed when click, confirm, and state verification succeed', async () => {
|
|
146
|
-
const page = makePage([
|
|
147
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
148
|
-
{ ok: true, state: 'unfollow-clicked' },
|
|
149
|
-
{ ok: true },
|
|
150
|
-
{ ok: true },
|
|
151
|
-
]);
|
|
152
|
-
const result = await getCommand().func(page, { 'user-id': validId });
|
|
153
|
-
expect(result).toEqual([{
|
|
154
|
-
status: 'unfollowed',
|
|
155
|
-
user_id: validId,
|
|
156
|
-
url: `https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
157
|
-
}]);
|
|
158
|
-
expect(page.goto).toHaveBeenCalledWith(`https://www.xiaohongshu.com/user/profile/${validId}`);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('returns not-following without modal confirmation when already unfollowed', async () => {
|
|
162
|
-
const page = makePage([
|
|
163
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
164
|
-
{ ok: true, state: 'not-following' },
|
|
165
|
-
]);
|
|
166
|
-
const result = await getCommand().func(page, { 'user-id': validId });
|
|
167
|
-
expect(result[0].status).toBe('not-following');
|
|
168
|
-
expect(page.evaluate).toHaveBeenCalledTimes(2);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('throws AuthRequiredError when xhs redirects unfollow to /login', async () => {
|
|
172
|
-
const page = makePage([
|
|
173
|
-
'https://www.xiaohongshu.com/login?redirectPath=/user/profile/' + validId,
|
|
174
|
-
]);
|
|
175
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toBeInstanceOf(AuthRequiredError);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('throws CommandExecutionError when unfollow navigation lands on a different profile', async () => {
|
|
179
|
-
const page = makePage([
|
|
180
|
-
'https://www.xiaohongshu.com/user/profile/5d8f88dc0000000001005d4b',
|
|
181
|
-
]);
|
|
182
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toThrowError(/expected profile/);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('throws CommandExecutionError when unfollow navigation lands on a non-Xiaohongshu host', async () => {
|
|
186
|
-
const page = makePage([
|
|
187
|
-
`https://evil.example/user/profile/${validId}`,
|
|
188
|
-
]);
|
|
189
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toThrowError(/expected Xiaohongshu profile host/);
|
|
190
|
-
expect(page.evaluate).toHaveBeenCalledTimes(1);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('throws CommandExecutionError when modal confirmation is missing', async () => {
|
|
194
|
-
const page = makePage([
|
|
195
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
196
|
-
{ ok: true, state: 'unfollow-clicked' },
|
|
197
|
-
{ ok: false, kind: 'no_modal' },
|
|
198
|
-
]);
|
|
199
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toThrowError(/confirmation modal/);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('throws CommandExecutionError when final unfollow verification fails', async () => {
|
|
203
|
-
const page = makePage([
|
|
204
|
-
`https://www.xiaohongshu.com/user/profile/${validId}`,
|
|
205
|
-
{ ok: true, state: 'unfollow-clicked' },
|
|
206
|
-
{ ok: true },
|
|
207
|
-
{ ok: false, reason: 'still following' },
|
|
208
|
-
]);
|
|
209
|
-
await expect(getCommand().func(page, { 'user-id': validId })).rejects.toThrowError(/still following/);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { cli, Strategy } from '@agentrhq/webcmd/registry';
|
|
2
|
-
import { fetchXhsCollectionNotes, LIKE_API_PATTERN, LIKED_PROFILE_TAB, parseCollectionLimit, resolveXhsUserId } from './collection-helpers.js';
|
|
3
|
-
|
|
4
|
-
cli({
|
|
5
|
-
site: 'xiaohongshu',
|
|
6
|
-
name: 'liked',
|
|
7
|
-
access: 'read',
|
|
8
|
-
description: '小红书赞过笔记列表',
|
|
9
|
-
domain: 'www.xiaohongshu.com',
|
|
10
|
-
strategy: Strategy.COOKIE,
|
|
11
|
-
navigateBefore: false,
|
|
12
|
-
browser: true,
|
|
13
|
-
args: [
|
|
14
|
-
{ name: 'id', type: 'string', help: 'User id or profile URL (defaults to current logged-in user)' },
|
|
15
|
-
{ name: 'limit', type: 'int', default: 20, help: 'Number of notes to return' },
|
|
16
|
-
],
|
|
17
|
-
columns: ['rank', 'id', 'title', 'author', 'likes', 'type', 'url'],
|
|
18
|
-
func: async (page, kwargs) => {
|
|
19
|
-
const limit = parseCollectionLimit(kwargs.limit);
|
|
20
|
-
const userId = await resolveXhsUserId(page, kwargs.id);
|
|
21
|
-
return fetchXhsCollectionNotes(page, {
|
|
22
|
-
userId,
|
|
23
|
-
profileTab: LIKED_PROFILE_TAB,
|
|
24
|
-
apiPattern: LIKE_API_PATTERN,
|
|
25
|
-
limit,
|
|
26
|
-
emptyLabel: 'liked',
|
|
27
|
-
});
|
|
28
|
-
},
|
|
29
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { CommandExecutionError } from '@agentrhq/webcmd/errors';
|
|
3
|
-
import { getRegistry } from '@agentrhq/webcmd/registry';
|
|
4
|
-
import { createPageMock } from '../test-utils.js';
|
|
5
|
-
import './liked.js';
|
|
6
|
-
|
|
7
|
-
describe('xiaohongshu liked', () => {
|
|
8
|
-
const command = getRegistry().get('xiaohongshu/liked');
|
|
9
|
-
|
|
10
|
-
it('registers with navigateBefore=false', () => {
|
|
11
|
-
expect(command).toBeDefined();
|
|
12
|
-
expect(command.navigateBefore).toBe(false);
|
|
13
|
-
expect(command.name).toBe('liked');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('captures liked notes from the like API', async () => {
|
|
17
|
-
const intercepted = [
|
|
18
|
-
{
|
|
19
|
-
data: {
|
|
20
|
-
notes: [
|
|
21
|
-
{
|
|
22
|
-
note_id: '662908190000000001007367',
|
|
23
|
-
xsec_token: 'tok-2',
|
|
24
|
-
note_card: {
|
|
25
|
-
display_title: '赞过笔记',
|
|
26
|
-
type: 'video',
|
|
27
|
-
user: { user_id: 'user-2', nickname: 'Bob' },
|
|
28
|
-
interact_info: { liked_count: '99' },
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
];
|
|
35
|
-
const evaluate = vi.fn()
|
|
36
|
-
.mockResolvedValueOnce(false)
|
|
37
|
-
.mockResolvedValueOnce('self-user')
|
|
38
|
-
.mockResolvedValueOnce(false)
|
|
39
|
-
.mockResolvedValueOnce({
|
|
40
|
-
hostname: 'www.xiaohongshu.com',
|
|
41
|
-
pathname: '/user/profile/self-user',
|
|
42
|
-
href: 'https://www.xiaohongshu.com/user/profile/self-user?tab=liked&subTab=note',
|
|
43
|
-
});
|
|
44
|
-
const getInterceptedRequests = vi.fn()
|
|
45
|
-
.mockResolvedValueOnce([])
|
|
46
|
-
.mockResolvedValueOnce(intercepted);
|
|
47
|
-
const page = createPageMock([], {
|
|
48
|
-
evaluate,
|
|
49
|
-
getInterceptedRequests,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const result = await command.func(page, { limit: 1 });
|
|
53
|
-
expect(result).toEqual([
|
|
54
|
-
{
|
|
55
|
-
rank: 1,
|
|
56
|
-
id: '662908190000000001007367',
|
|
57
|
-
title: '赞过笔记',
|
|
58
|
-
author: 'Bob',
|
|
59
|
-
likes: '99',
|
|
60
|
-
type: 'video',
|
|
61
|
-
url: 'https://www.xiaohongshu.com/user/profile/user-2/662908190000000001007367?xsec_token=tok-2&xsec_source=pc_user',
|
|
62
|
-
},
|
|
63
|
-
]);
|
|
64
|
-
expect(page.installInterceptor).toHaveBeenCalledWith('note/like/page');
|
|
65
|
-
expect(page.goto.mock.calls.at(-1)[0]).toBe('https://www.xiaohongshu.com/user/profile/self-user?tab=liked&subTab=note');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('fails closed when navigation lands on another profile', async () => {
|
|
69
|
-
const page = createPageMock([], {
|
|
70
|
-
evaluate: vi.fn()
|
|
71
|
-
.mockResolvedValueOnce(false)
|
|
72
|
-
.mockResolvedValueOnce({
|
|
73
|
-
hostname: 'www.xiaohongshu.com',
|
|
74
|
-
pathname: '/user/profile/other-user',
|
|
75
|
-
href: 'https://www.xiaohongshu.com/user/profile/other-user?tab=liked&subTab=note',
|
|
76
|
-
}),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
await expect(command.func(page, { id: 'self-user', limit: 5 })).rejects.toBeInstanceOf(CommandExecutionError);
|
|
80
|
-
expect(page.getInterceptedRequests).not.toHaveBeenCalled();
|
|
81
|
-
});
|
|
82
|
-
});
|