* feat(rednote): add rednote.com adapter mirroring xiaohongshu read commands (#1136) Implements rednote.com support as discussed in issue #1136. The mainland xiaohongshu adapter stays in place; international users redirected to www.rednote.com now have a CLI without a copy-pasted adapter. Issue #1136 documents that xiaohongshu and rednote share DOM selectors, URL paths, API paths, response schema, cookies, and the xsec_token auth mechanism. The only material differences: Layer xiaohongshu rednote Web host www.xiaohongshu.com www.rednote.com API host edith.xiaohongshu.com webapi.rednote.com Security host fe-static.xhscdn.com as.rednote.com Cookie root .xiaohongshu.com .rednote.com Search gate Inline text Full-screen modal + text ## Architecture (minimal) `clis/xiaohongshu/*` keep all selector / regex / extraction logic. Each command file is touched minimally to export the IIFE or pipeline so the sibling adapter can reuse it: search.js + export const buildSearchExtractJs(webHost) + export const command = cli({...}) note.js + export const NOTE_EXTRACT_JS + export const command = cli({...}) comments.js + export function buildCommentsExtractJs(withReplies) + export parseCommentLimit + export const command = cli({...}) download.js + export function buildDownloadExtractJs(noteId) (CDN allowlist now includes rednote alongside xhscdn) + export const command = cli({...}) user.js + export const USER_SNAPSHOT_JS + export const command = cli({...}) feed.js + export function buildFeedPipeline(webHost) + export const command = cli({...}) notifications.js + export function buildNotificationsPipeline(webHost) + export const command = cli({...}) note-helpers.js buildNoteUrl now accepts `cookieRoot` + `signedUrlHint` options (defaults preserved so xhs callers and tests are unchanged) user-helpers.js buildXhsNoteUrl / extractXhsUserNotes accept an optional `webHost` argument (default xhs) The `export const command = cli({...})` pattern matches twitter/lists.js and clis/discord-app/*; without it the build-manifest scanner attributes xhs's command to whichever rednote sibling triggered the transitive import first. ## clis/rednote/ — thin shims Each rednote command file imports the relevant builder / constant from its xiaohongshu sibling and calls `cli()` with the rednote host triple. No selectors, regexes, or extraction logic are duplicated. search.js imports buildSearchExtractJs + noteIdToDate declares its own WAIT_FOR_CONTENT_JS (modal + text login-gate variants — the one xhs behaviour that genuinely differs) note.js imports NOTE_EXTRACT_JS + buildNoteUrl + parseNoteId comments.js imports buildCommentsExtractJs + parseCommentLimit + buildNoteUrl + parseNoteId download.js imports buildDownloadExtractJs + buildNoteUrl + parseNoteId user.js imports USER_SNAPSHOT_JS + extractXhsUserNotes + normalizeXhsUserId ## Scope (initial) Ships the five commands verified live against the user's logged-in rednote.com session: search / note / comments / user / download. `feed` and `notifications` are intentionally left out. Both rely on intercepting the xiaohongshu Pinia store at the `homefeed` / `you` capture pattern; live verification on rednote returns `tap → dict (error)` for the feed step, so shipping them would surface a broken contract. The mainland xiaohongshu commands continue to work. Adding the rednote-side feed / notifications is straightforward follow-up work once someone with rednote access maps the network surface. Creator-center commands (publish, creator-*) have no rednote counterpart and stay xiaohongshu-only, per the reporter's note in #1136. ## Verification - clis/xiaohongshu/ + clis/rednote/: 103/103 tests green - npx tsc --noEmit: clean - npm run build: 807 manifest entries (xhs 13 + rednote 5 + everything else preserved) - silent-column-drop / typed-error-lint: 103 / 189 baseline entries, no new violations - Live verify against the user's rednote.com session: rednote search "travel" --limit 1 → real note row rednote note <signed-url> → 7 field/value rows rednote comments <signed-url> --limit 3 → 3 top-level rows rednote user 5b21f6564eacab3b38f05c39 --limit 2 → 2 profile notes Spaced 15–30s between runs per the xhs/rednote rate-limit guidance; no write commands invoked. Regression check: xiaohongshu/feed on the existing mainland session still returns the standard 6-field rows after the refactor. Closes #1136 * fix(rednote): tighten adapter failure boundaries --------- Co-authored-by: jackwener <jakevingoo@gmail.com>
3.5 KiB
Rednote (国际版小红书)
Mode: 🔐 Browser · Domain: www.rednote.com (cookie root .rednote.com)
Rednote is the international mirror of Xiaohongshu. Logged-in users outside mainland China are redirected from www.xiaohongshu.com to www.rednote.com, where the codebase is largely the same but hostnames and cookie root differ. This adapter exists so those users have working CLI commands without juggling the mainland-domain xiaohongshu adapter. See issue #1136 for the host-by-host comparison.
Commands
| Command | Description |
|---|---|
opencli rednote search |
Search notes by keyword (returns title, author, likes, URL, author URL) |
opencli rednote note |
Read full note content (title, author, description, likes, collects, comments, tags) |
opencli rednote comments |
Read comments from a note (--with-replies for nested 楼中楼 replies) |
opencli rednote user |
Get public notes from a user profile |
opencli rednote feed |
Home feed (reads the hydrated Pinia store) |
opencli rednote notifications |
Notifications (`--type mentions |
opencli rednote download |
Download images and videos from a note |
Usage Examples
# Search for notes
opencli rednote search travel --limit 10
# Read a note's full content (pass URL from search results to preserve xsec_token)
opencli rednote note "https://www.rednote.com/search_result/<id>?xsec_token=..."
# Read comments with nested replies (楼中楼)
opencli rednote comments "https://www.rednote.com/search_result/<id>?xsec_token=..." --with-replies --limit 20
# JSON output
opencli rednote search travel -f json
# User profile notes
opencli rednote user 5b21f6564eacab3b38f05c39 --limit 10
# Download
opencli rednote download "https://www.rednote.com/search_result/<id>?xsec_token=..."
Note:
note,comments, anddownloadrequire a full signed rednote.com note URL withxsec_token. Bare note IDs and xhslink.com short links are not accepted because they cannot prove the rednote host/cookie identity before navigation.
Prerequisites
- Chrome running and logged into rednote.com
- Browser Bridge extension installed
Implementation
The rednote command files are thin shims that import the DOM-extraction IIFEs and URL helpers from their clis/xiaohongshu/* counterparts and call cli() with the rednote host triple. There is no duplicate copy of selectors, regexes, or extraction logic.
| Layer | xiaohongshu | rednote |
|---|---|---|
| Web host | www.xiaohongshu.com |
www.rednote.com |
| API host | edith.xiaohongshu.com |
webapi.rednote.com |
| Security/signing host | fe-static.xhscdn.com |
as.rednote.com |
| Cookie root | .xiaohongshu.com |
.rednote.com |
| Search login gate | Inline 登录后查看搜索结果 text |
Full-screen login modal (plus the inline text as a fallback) |
rednote feed and rednote notifications cannot share the xiaohongshu intercept pipeline. rednote's feed.feeds is populated from SSR and the field names are camelCase (noteCard.displayTitle, interactInfo.likedCount) rather than xhs's snake_case API shape, so the xhs tap captures nothing. notification.notificationMap[<type>].messageList is filled by the in-page getNotification() action without firing a network request the tap can match. Both rednote commands therefore read the hydrated Pinia store in func mode, accepting both casing conventions when extracting fields. Creator-center commands (publish, creator-*) have no rednote counterpart and stay xiaohongshu-only.