@zenalexa/unicli 0.220.1 → 0.221.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +35 -13
- package/README.md +18 -16
- package/README.zh-CN.md +18 -16
- package/dist/adapters/acl-anthology/papers.d.ts +16 -0
- package/dist/adapters/acl-anthology/papers.d.ts.map +1 -0
- package/dist/adapters/acl-anthology/papers.js +135 -0
- package/dist/adapters/acl-anthology/papers.js.map +1 -0
- package/dist/adapters/arxiv/papers.js +2 -0
- package/dist/adapters/arxiv/papers.js.map +1 -1
- package/dist/adapters/baidu-scholar/search.js +5 -0
- package/dist/adapters/baidu-scholar/search.js.map +1 -1
- package/dist/adapters/bilibili/comments.js +66 -4
- package/dist/adapters/bilibili/comments.js.map +1 -1
- package/dist/adapters/bilibili/compat.js +2 -2
- package/dist/adapters/bilibili/compat.js.map +1 -1
- package/dist/adapters/bilibili/download.js +4 -4
- package/dist/adapters/bilibili/download.js.map +1 -1
- package/dist/adapters/bilibili/wbi.d.ts.map +1 -1
- package/dist/adapters/bilibili/wbi.js +3 -3
- package/dist/adapters/bilibili/wbi.js.map +1 -1
- package/dist/adapters/cipo/_shared.d.ts +21 -0
- package/dist/adapters/cipo/_shared.d.ts.map +1 -0
- package/dist/adapters/cipo/_shared.js +67 -0
- package/dist/adapters/cipo/_shared.js.map +1 -0
- package/dist/adapters/cipo/get.d.ts +19 -0
- package/dist/adapters/cipo/get.d.ts.map +1 -0
- package/dist/adapters/cipo/get.js +140 -0
- package/dist/adapters/cipo/get.js.map +1 -0
- package/dist/adapters/cipo/legal-status.d.ts +19 -0
- package/dist/adapters/cipo/legal-status.d.ts.map +1 -0
- package/dist/adapters/cipo/legal-status.js +111 -0
- package/dist/adapters/cipo/legal-status.js.map +1 -0
- package/dist/adapters/cipo/search.d.ts +20 -0
- package/dist/adapters/cipo/search.d.ts.map +1 -0
- package/dist/adapters/cipo/search.js +148 -0
- package/dist/adapters/cipo/search.js.map +1 -0
- package/dist/adapters/cnipa/_shared.d.ts +47 -0
- package/dist/adapters/cnipa/_shared.d.ts.map +1 -0
- package/dist/adapters/cnipa/_shared.js +97 -0
- package/dist/adapters/cnipa/_shared.js.map +1 -0
- package/dist/adapters/cnipa/get.d.ts +19 -0
- package/dist/adapters/cnipa/get.d.ts.map +1 -0
- package/dist/adapters/cnipa/get.js +149 -0
- package/dist/adapters/cnipa/get.js.map +1 -0
- package/dist/adapters/cnipa/legal-status.d.ts +19 -0
- package/dist/adapters/cnipa/legal-status.d.ts.map +1 -0
- package/dist/adapters/cnipa/legal-status.js +119 -0
- package/dist/adapters/cnipa/legal-status.js.map +1 -0
- package/dist/adapters/cnipa/search.d.ts +21 -0
- package/dist/adapters/cnipa/search.d.ts.map +1 -0
- package/dist/adapters/cnipa/search.js +170 -0
- package/dist/adapters/cnipa/search.js.map +1 -0
- package/dist/adapters/crossref/works.d.ts +42 -0
- package/dist/adapters/crossref/works.d.ts.map +1 -0
- package/dist/adapters/crossref/works.js +157 -0
- package/dist/adapters/crossref/works.js.map +1 -0
- package/dist/adapters/cvf/papers.d.ts +17 -0
- package/dist/adapters/cvf/papers.d.ts.map +1 -0
- package/dist/adapters/cvf/papers.js +124 -0
- package/dist/adapters/cvf/papers.js.map +1 -0
- package/dist/adapters/dblp/publications.js +4 -0
- package/dist/adapters/dblp/publications.js.map +1 -1
- package/dist/adapters/espacenet/_shared.d.ts +21 -0
- package/dist/adapters/espacenet/_shared.d.ts.map +1 -0
- package/dist/adapters/espacenet/_shared.js +67 -0
- package/dist/adapters/espacenet/_shared.js.map +1 -0
- package/dist/adapters/espacenet/family.d.ts +19 -0
- package/dist/adapters/espacenet/family.d.ts.map +1 -0
- package/dist/adapters/espacenet/family.js +118 -0
- package/dist/adapters/espacenet/family.js.map +1 -0
- package/dist/adapters/espacenet/get.d.ts +19 -0
- package/dist/adapters/espacenet/get.d.ts.map +1 -0
- package/dist/adapters/espacenet/get.js +130 -0
- package/dist/adapters/espacenet/get.js.map +1 -0
- package/dist/adapters/espacenet/legal-status.d.ts +19 -0
- package/dist/adapters/espacenet/legal-status.d.ts.map +1 -0
- package/dist/adapters/espacenet/legal-status.js +110 -0
- package/dist/adapters/espacenet/legal-status.js.map +1 -0
- package/dist/adapters/espacenet/search.d.ts +20 -0
- package/dist/adapters/espacenet/search.d.ts.map +1 -0
- package/dist/adapters/espacenet/search.js +165 -0
- package/dist/adapters/espacenet/search.js.map +1 -0
- package/dist/adapters/facebook/subtitles.d.ts +9 -0
- package/dist/adapters/facebook/subtitles.d.ts.map +1 -0
- package/dist/adapters/facebook/subtitles.js +42 -0
- package/dist/adapters/facebook/subtitles.js.map +1 -0
- package/dist/adapters/fips/_shared.d.ts +21 -0
- package/dist/adapters/fips/_shared.d.ts.map +1 -0
- package/dist/adapters/fips/_shared.js +77 -0
- package/dist/adapters/fips/_shared.js.map +1 -0
- package/dist/adapters/fips/get.d.ts +19 -0
- package/dist/adapters/fips/get.d.ts.map +1 -0
- package/dist/adapters/fips/get.js +139 -0
- package/dist/adapters/fips/get.js.map +1 -0
- package/dist/adapters/fips/search.d.ts +20 -0
- package/dist/adapters/fips/search.d.ts.map +1 -0
- package/dist/adapters/fips/search.js +148 -0
- package/dist/adapters/fips/search.js.map +1 -0
- package/dist/adapters/freepatentsonline-web/_shared.d.ts +72 -0
- package/dist/adapters/freepatentsonline-web/_shared.d.ts.map +1 -0
- package/dist/adapters/freepatentsonline-web/_shared.js +216 -0
- package/dist/adapters/freepatentsonline-web/_shared.js.map +1 -0
- package/dist/adapters/freepatentsonline-web/get.d.ts +21 -0
- package/dist/adapters/freepatentsonline-web/get.d.ts.map +1 -0
- package/dist/adapters/freepatentsonline-web/get.js +127 -0
- package/dist/adapters/freepatentsonline-web/get.js.map +1 -0
- package/dist/adapters/freepatentsonline-web/search.d.ts +22 -0
- package/dist/adapters/freepatentsonline-web/search.d.ts.map +1 -0
- package/dist/adapters/freepatentsonline-web/search.js +149 -0
- package/dist/adapters/freepatentsonline-web/search.js.map +1 -0
- package/dist/adapters/google-patents-web/_shared.d.ts +110 -0
- package/dist/adapters/google-patents-web/_shared.d.ts.map +1 -0
- package/dist/adapters/google-patents-web/_shared.js +164 -0
- package/dist/adapters/google-patents-web/_shared.js.map +1 -0
- package/dist/adapters/google-patents-web/get.d.ts +36 -0
- package/dist/adapters/google-patents-web/get.d.ts.map +1 -0
- package/dist/adapters/google-patents-web/get.js +187 -0
- package/dist/adapters/google-patents-web/get.js.map +1 -0
- package/dist/adapters/google-patents-web/search.d.ts +23 -0
- package/dist/adapters/google-patents-web/search.d.ts.map +1 -0
- package/dist/adapters/google-patents-web/search.js +169 -0
- package/dist/adapters/google-patents-web/search.js.map +1 -0
- package/dist/adapters/google-scholar/cite.js +1 -0
- package/dist/adapters/google-scholar/cite.js.map +1 -1
- package/dist/adapters/google-scholar/profile.js +5 -0
- package/dist/adapters/google-scholar/profile.js.map +1 -1
- package/dist/adapters/google-scholar/search.js +5 -0
- package/dist/adapters/google-scholar/search.js.map +1 -1
- package/dist/adapters/hf/paper.js +1 -0
- package/dist/adapters/hf/paper.js.map +1 -1
- package/dist/adapters/inpi-br/_shared.d.ts +21 -0
- package/dist/adapters/inpi-br/_shared.d.ts.map +1 -0
- package/dist/adapters/inpi-br/_shared.js +67 -0
- package/dist/adapters/inpi-br/_shared.js.map +1 -0
- package/dist/adapters/inpi-br/get.d.ts +19 -0
- package/dist/adapters/inpi-br/get.d.ts.map +1 -0
- package/dist/adapters/inpi-br/get.js +142 -0
- package/dist/adapters/inpi-br/get.js.map +1 -0
- package/dist/adapters/inpi-br/search.d.ts +20 -0
- package/dist/adapters/inpi-br/search.d.ts.map +1 -0
- package/dist/adapters/inpi-br/search.js +154 -0
- package/dist/adapters/inpi-br/search.js.map +1 -0
- package/dist/adapters/instagram/subtitles.d.ts +9 -0
- package/dist/adapters/instagram/subtitles.d.ts.map +1 -0
- package/dist/adapters/instagram/subtitles.js +42 -0
- package/dist/adapters/instagram/subtitles.js.map +1 -0
- package/dist/adapters/mastodon/statuses.d.ts +40 -0
- package/dist/adapters/mastodon/statuses.d.ts.map +1 -0
- package/dist/adapters/mastodon/statuses.js +153 -0
- package/dist/adapters/mastodon/statuses.js.map +1 -0
- package/dist/adapters/neurips/proceedings.d.ts +17 -0
- package/dist/adapters/neurips/proceedings.d.ts.map +1 -0
- package/dist/adapters/neurips/proceedings.js +112 -0
- package/dist/adapters/neurips/proceedings.js.map +1 -0
- package/dist/adapters/openalex/works.d.ts.map +1 -1
- package/dist/adapters/openalex/works.js +32 -0
- package/dist/adapters/openalex/works.js.map +1 -1
- package/dist/adapters/openreview/papers.js +5 -0
- package/dist/adapters/openreview/papers.js.map +1 -1
- package/dist/adapters/pmlr/proceedings.d.ts +35 -0
- package/dist/adapters/pmlr/proceedings.d.ts.map +1 -0
- package/dist/adapters/pmlr/proceedings.js +139 -0
- package/dist/adapters/pmlr/proceedings.js.map +1 -0
- package/dist/adapters/pubmed/articles.js +5 -0
- package/dist/adapters/pubmed/articles.js.map +1 -1
- package/dist/adapters/reddit/comments.d.ts +9 -0
- package/dist/adapters/reddit/comments.d.ts.map +1 -0
- package/dist/adapters/reddit/comments.js +124 -0
- package/dist/adapters/reddit/comments.js.map +1 -0
- package/dist/adapters/semantic-scholar/papers.d.ts +36 -0
- package/dist/adapters/semantic-scholar/papers.d.ts.map +1 -0
- package/dist/adapters/semantic-scholar/papers.js +214 -0
- package/dist/adapters/semantic-scholar/papers.js.map +1 -0
- package/dist/adapters/threads/post.d.ts +32 -0
- package/dist/adapters/threads/post.d.ts.map +1 -0
- package/dist/adapters/threads/post.js +287 -0
- package/dist/adapters/threads/post.js.map +1 -0
- package/dist/adapters/tiktok/subtitles.d.ts +9 -0
- package/dist/adapters/tiktok/subtitles.d.ts.map +1 -0
- package/dist/adapters/tiktok/subtitles.js +42 -0
- package/dist/adapters/tiktok/subtitles.js.map +1 -0
- package/dist/adapters/twitter/accept.js +2 -2
- package/dist/adapters/twitter/accept.js.map +1 -1
- package/dist/adapters/twitter/browser-fallback.d.ts +26 -0
- package/dist/adapters/twitter/browser-fallback.d.ts.map +1 -0
- package/dist/adapters/twitter/browser-fallback.js +93 -0
- package/dist/adapters/twitter/browser-fallback.js.map +1 -0
- package/dist/adapters/twitter/browser-state.d.ts +11 -0
- package/dist/adapters/twitter/browser-state.d.ts.map +1 -0
- package/dist/adapters/twitter/browser-state.js +46 -0
- package/dist/adapters/twitter/browser-state.js.map +1 -0
- package/dist/adapters/twitter/client.d.ts.map +1 -1
- package/dist/adapters/twitter/client.js +36 -13
- package/dist/adapters/twitter/client.js.map +1 -1
- package/dist/adapters/twitter/reply-dm.js +2 -2
- package/dist/adapters/twitter/reply-dm.js.map +1 -1
- package/dist/adapters/twitter/reply.js +1 -0
- package/dist/adapters/twitter/reply.js.map +1 -1
- package/dist/adapters/twitter/search.js +11 -18
- package/dist/adapters/twitter/search.js.map +1 -1
- package/dist/adapters/twitter/thread.d.ts +14 -0
- package/dist/adapters/twitter/thread.d.ts.map +1 -1
- package/dist/adapters/twitter/thread.js +28 -2
- package/dist/adapters/twitter/thread.js.map +1 -1
- package/dist/adapters/twitter/trending.js +13 -59
- package/dist/adapters/twitter/trending.js.map +1 -1
- package/dist/adapters/unpaywall/works.d.ts +33 -0
- package/dist/adapters/unpaywall/works.d.ts.map +1 -0
- package/dist/adapters/unpaywall/works.js +101 -0
- package/dist/adapters/unpaywall/works.js.map +1 -0
- package/dist/adapters/xiaohongshu/browser-state.d.ts +19 -0
- package/dist/adapters/xiaohongshu/browser-state.d.ts.map +1 -0
- package/dist/adapters/xiaohongshu/browser-state.js +67 -0
- package/dist/adapters/xiaohongshu/browser-state.js.map +1 -0
- package/dist/adapters/xiaohongshu/comments.js +28 -5
- package/dist/adapters/xiaohongshu/comments.js.map +1 -1
- package/dist/adapters/xiaohongshu/download.js +49 -11
- package/dist/adapters/xiaohongshu/download.js.map +1 -1
- package/dist/adapters/xiaohongshu/search.d.ts.map +1 -1
- package/dist/adapters/xiaohongshu/search.js +11 -5
- package/dist/adapters/xiaohongshu/search.js.map +1 -1
- package/dist/adapters/xiaohongshu/trending.d.ts +9 -0
- package/dist/adapters/xiaohongshu/trending.d.ts.map +1 -0
- package/dist/adapters/xiaohongshu/trending.js +94 -0
- package/dist/adapters/xiaohongshu/trending.js.map +1 -0
- package/dist/adapters/youtube/comments.d.ts +80 -0
- package/dist/adapters/youtube/comments.d.ts.map +1 -1
- package/dist/adapters/youtube/comments.js +108 -12
- package/dist/adapters/youtube/comments.js.map +1 -1
- package/dist/adapters/youtube/subtitles.d.ts +9 -0
- package/dist/adapters/youtube/subtitles.d.ts.map +1 -0
- package/dist/adapters/youtube/subtitles.js +42 -0
- package/dist/adapters/youtube/subtitles.js.map +1 -0
- package/dist/adapters/yt-dlp/subtitles.d.ts +9 -0
- package/dist/adapters/yt-dlp/subtitles.d.ts.map +1 -0
- package/dist/adapters/yt-dlp/subtitles.js +41 -0
- package/dist/adapters/yt-dlp/subtitles.js.map +1 -0
- package/dist/adapters/zhihu/answer-detail.d.ts +39 -0
- package/dist/adapters/zhihu/answer-detail.d.ts.map +1 -0
- package/dist/adapters/zhihu/answer-detail.js +204 -0
- package/dist/adapters/zhihu/answer-detail.js.map +1 -0
- package/dist/adapters/zhihu/comment.d.ts +9 -0
- package/dist/adapters/zhihu/comment.d.ts.map +1 -0
- package/dist/adapters/zhihu/comment.js +149 -0
- package/dist/adapters/zhihu/comment.js.map +1 -0
- package/dist/adapters/zhihu/recommend.d.ts +36 -0
- package/dist/adapters/zhihu/recommend.d.ts.map +1 -0
- package/dist/adapters/zhihu/recommend.js +151 -0
- package/dist/adapters/zhihu/recommend.js.map +1 -0
- package/dist/browser/bridge.d.ts.map +1 -1
- package/dist/browser/bridge.js +14 -3
- package/dist/browser/bridge.js.map +1 -1
- package/dist/browser/daemon-client.d.ts +6 -0
- package/dist/browser/daemon-client.d.ts.map +1 -1
- package/dist/browser/daemon-client.js +75 -15
- package/dist/browser/daemon-client.js.map +1 -1
- package/dist/browser/daemon.js +39 -15
- package/dist/browser/daemon.js.map +1 -1
- package/dist/browser/protocol.d.ts +1 -0
- package/dist/browser/protocol.d.ts.map +1 -1
- package/dist/browser/protocol.js +1 -0
- package/dist/browser/protocol.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +21 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/approvals.d.ts.map +1 -1
- package/dist/commands/approvals.js +1 -37
- package/dist/commands/approvals.js.map +1 -1
- package/dist/commands/browser/index.d.ts.map +1 -1
- package/dist/commands/browser/index.js +7 -2
- package/dist/commands/browser/index.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +7 -3
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/dispatch.js +27 -3
- package/dist/commands/dispatch.js.map +1 -1
- package/dist/commands/do.d.ts +30 -0
- package/dist/commands/do.d.ts.map +1 -0
- package/dist/commands/do.js +248 -0
- package/dist/commands/do.js.map +1 -0
- package/dist/commands/extract.d.ts +34 -0
- package/dist/commands/extract.d.ts.map +1 -0
- package/dist/commands/extract.js +316 -0
- package/dist/commands/extract.js.map +1 -0
- package/dist/commands/patent-doctor.d.ts +48 -0
- package/dist/commands/patent-doctor.d.ts.map +1 -0
- package/dist/commands/patent-doctor.js +109 -0
- package/dist/commands/patent-doctor.js.map +1 -0
- package/dist/commands/patent.d.ts +78 -0
- package/dist/commands/patent.d.ts.map +1 -0
- package/dist/commands/patent.js +919 -0
- package/dist/commands/patent.js.map +1 -0
- package/dist/commands/scholar.d.ts +33 -0
- package/dist/commands/scholar.d.ts.map +1 -0
- package/dist/commands/scholar.js +494 -0
- package/dist/commands/scholar.js.map +1 -0
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +2 -5
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/social.d.ts +19 -0
- package/dist/commands/social.d.ts.map +1 -0
- package/dist/commands/social.js +236 -0
- package/dist/commands/social.js.map +1 -0
- package/dist/core/registry.d.ts +1 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +11 -2
- package/dist/core/registry.js.map +1 -1
- package/dist/discovery/aliases.d.ts +2 -2
- package/dist/discovery/aliases.d.ts.map +1 -1
- package/dist/discovery/aliases.js +182 -11
- package/dist/discovery/aliases.js.map +1 -1
- package/dist/discovery/intents.d.ts +10 -0
- package/dist/discovery/intents.d.ts.map +1 -0
- package/dist/discovery/intents.js +255 -0
- package/dist/discovery/intents.js.map +1 -0
- package/dist/discovery/loader.d.ts.map +1 -1
- package/dist/discovery/loader.js +4 -0
- package/dist/discovery/loader.js.map +1 -1
- package/dist/discovery/search.d.ts +4 -1
- package/dist/discovery/search.d.ts.map +1 -1
- package/dist/discovery/search.js +28 -140
- package/dist/discovery/search.js.map +1 -1
- package/dist/engine/approval-presenter.d.ts +10 -0
- package/dist/engine/approval-presenter.d.ts.map +1 -0
- package/dist/engine/approval-presenter.js +45 -0
- package/dist/engine/approval-presenter.js.map +1 -0
- package/dist/engine/approval-store.d.ts +4 -0
- package/dist/engine/approval-store.d.ts.map +1 -1
- package/dist/engine/approval-store.js +85 -11
- package/dist/engine/approval-store.js.map +1 -1
- package/dist/engine/auth/oauth2-cc.d.ts +67 -0
- package/dist/engine/auth/oauth2-cc.d.ts.map +1 -0
- package/dist/engine/auth/oauth2-cc.js +120 -0
- package/dist/engine/auth/oauth2-cc.js.map +1 -0
- package/dist/engine/cookies.d.ts +10 -0
- package/dist/engine/cookies.d.ts.map +1 -1
- package/dist/engine/cookies.js +64 -0
- package/dist/engine/cookies.js.map +1 -1
- package/dist/engine/download.d.ts +5 -0
- package/dist/engine/download.d.ts.map +1 -1
- package/dist/engine/download.js +11 -4
- package/dist/engine/download.js.map +1 -1
- package/dist/engine/executor.d.ts +1 -0
- package/dist/engine/executor.d.ts.map +1 -1
- package/dist/engine/executor.js +25 -0
- package/dist/engine/executor.js.map +1 -1
- package/dist/engine/framework.d.ts +5 -5
- package/dist/engine/framework.js +5 -5
- package/dist/engine/harden.d.ts +1 -1
- package/dist/engine/harden.js +1 -1
- package/dist/engine/kernel/stages.d.ts.map +1 -1
- package/dist/engine/kernel/stages.js +2 -1
- package/dist/engine/kernel/stages.js.map +1 -1
- package/dist/engine/normalizer/patent-envelope.d.ts +61 -0
- package/dist/engine/normalizer/patent-envelope.d.ts.map +1 -0
- package/dist/engine/normalizer/patent-envelope.js +132 -0
- package/dist/engine/normalizer/patent-envelope.js.map +1 -0
- package/dist/engine/research.d.ts +5 -7
- package/dist/engine/research.d.ts.map +1 -1
- package/dist/engine/research.js +6 -9
- package/dist/engine/research.js.map +1 -1
- package/dist/engine/steps/browser-helpers.d.ts +2 -2
- package/dist/engine/steps/browser-helpers.d.ts.map +1 -1
- package/dist/engine/steps/browser-helpers.js +39 -16
- package/dist/engine/steps/browser-helpers.js.map +1 -1
- package/dist/engine/steps/download.d.ts +1 -0
- package/dist/engine/steps/download.d.ts.map +1 -1
- package/dist/engine/steps/download.js +3 -1
- package/dist/engine/steps/download.js.map +1 -1
- package/dist/engine/steps/index.d.ts +2 -0
- package/dist/engine/steps/index.d.ts.map +1 -1
- package/dist/engine/steps/index.js +2 -0
- package/dist/engine/steps/index.js.map +1 -1
- package/dist/engine/steps/oauth2-token.d.ts +41 -0
- package/dist/engine/steps/oauth2-token.d.ts.map +1 -0
- package/dist/engine/steps/oauth2-token.js +115 -0
- package/dist/engine/steps/oauth2-token.js.map +1 -0
- package/dist/engine/steps/select-xml.d.ts +34 -0
- package/dist/engine/steps/select-xml.d.ts.map +1 -0
- package/dist/engine/steps/select-xml.js +222 -0
- package/dist/engine/steps/select-xml.js.map +1 -0
- package/dist/engine/template.d.ts.map +1 -1
- package/dist/engine/template.js +7 -0
- package/dist/engine/template.js.map +1 -1
- package/dist/engine/transport/mcp-browser.d.ts +128 -0
- package/dist/engine/transport/mcp-browser.d.ts.map +1 -0
- package/dist/engine/transport/mcp-browser.js +120 -0
- package/dist/engine/transport/mcp-browser.js.map +1 -0
- package/dist/fast-path/handlers/approvals.d.ts +11 -0
- package/dist/fast-path/handlers/approvals.d.ts.map +1 -0
- package/dist/fast-path/handlers/approvals.js +136 -0
- package/dist/fast-path/handlers/approvals.js.map +1 -0
- package/dist/fast-path/handlers/discovery.d.ts.map +1 -1
- package/dist/fast-path/handlers/discovery.js +17 -3
- package/dist/fast-path/handlers/discovery.js.map +1 -1
- package/dist/fast-path/manifest.d.ts +1 -0
- package/dist/fast-path/manifest.d.ts.map +1 -1
- package/dist/fast-path/manifest.js.map +1 -1
- package/dist/fast-path.d.ts.map +1 -1
- package/dist/fast-path.js +3 -0
- package/dist/fast-path.js.map +1 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest-compact.txt +13 -11
- package/dist/manifest-search.json +1 -1
- package/dist/manifest.json +2560 -103
- package/dist/mcp/handler.d.ts.map +1 -1
- package/dist/mcp/handler.js +14 -2
- package/dist/mcp/handler.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +11 -3
- package/dist/mcp/tools.js.map +1 -1
- package/dist/output/auth-guidance.d.ts +14 -0
- package/dist/output/auth-guidance.d.ts.map +1 -0
- package/dist/output/auth-guidance.js +50 -0
- package/dist/output/auth-guidance.js.map +1 -0
- package/dist/output/error-map.d.ts +1 -1
- package/dist/output/error-map.d.ts.map +1 -1
- package/dist/output/error-map.js +28 -4
- package/dist/output/error-map.js.map +1 -1
- package/dist/output/next-actions.d.ts.map +1 -1
- package/dist/output/next-actions.js +19 -3
- package/dist/output/next-actions.js.map +1 -1
- package/dist/registry.d.ts +19 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +10 -0
- package/dist/registry.js.map +1 -1
- package/dist/social/browser-errors.d.ts +13 -0
- package/dist/social/browser-errors.d.ts.map +1 -0
- package/dist/social/browser-errors.js +36 -0
- package/dist/social/browser-errors.js.map +1 -0
- package/dist/social/capabilities.d.ts +29 -0
- package/dist/social/capabilities.d.ts.map +1 -0
- package/dist/social/capabilities.js +448 -0
- package/dist/social/capabilities.js.map +1 -0
- package/dist/social/comments.d.ts +26 -0
- package/dist/social/comments.d.ts.map +1 -0
- package/dist/social/comments.js +97 -0
- package/dist/social/comments.js.map +1 -0
- package/dist/social/video-text.d.ts +27 -0
- package/dist/social/video-text.d.ts.map +1 -0
- package/dist/social/video-text.js +140 -0
- package/dist/social/video-text.js.map +1 -0
- package/dist/types/patent.d.ts +160 -0
- package/dist/types/patent.d.ts.map +1 -0
- package/dist/types/patent.js +16 -0
- package/dist/types/patent.js.map +1 -0
- package/dist/types/scholarly.d.ts +49 -0
- package/dist/types/scholarly.d.ts.map +1 -0
- package/dist/types/scholarly.js +16 -0
- package/dist/types/scholarly.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -4
- package/server.json +3 -3
- package/skills/unicli/SKILL.md +1 -1
- package/skills/unicli-claude-code/SKILL.md +1 -1
- package/skills/unicli-hermes/SKILL.md +1 -1
- package/src/adapters/acl-anthology/papers.ts +157 -0
- package/src/adapters/arxiv/download.yaml +1 -1
- package/src/adapters/arxiv/paper.yaml +1 -1
- package/src/adapters/arxiv/papers.ts +2 -0
- package/src/adapters/arxiv/search.yaml +1 -1
- package/src/adapters/arxiv/trending.yaml +1 -1
- package/src/adapters/baidu-scholar/search.ts +5 -0
- package/src/adapters/bilibili/comments-tree.test.ts +41 -0
- package/src/adapters/bilibili/comments.ts +78 -4
- package/src/adapters/bilibili/compat.ts +5 -2
- package/src/adapters/bilibili/download.ts +7 -4
- package/src/adapters/bilibili/wbi.ts +6 -3
- package/src/adapters/brave/search.yaml +53 -0
- package/src/adapters/cipo/_shared.ts +98 -0
- package/src/adapters/cipo/get.ts +188 -0
- package/src/adapters/cipo/legal-status.ts +148 -0
- package/src/adapters/cipo/search.ts +195 -0
- package/src/adapters/cnipa/_shared.ts +138 -0
- package/src/adapters/cnipa/get.ts +199 -0
- package/src/adapters/cnipa/legal-status.ts +162 -0
- package/src/adapters/cnipa/search.ts +229 -0
- package/src/adapters/crossref/works.ts +209 -0
- package/src/adapters/cvf/papers.ts +136 -0
- package/src/adapters/dblp/publications.ts +4 -0
- package/src/adapters/dpma/get.yaml +67 -0
- package/src/adapters/dpma/search.yaml +77 -0
- package/src/adapters/duckduckgo/search.yaml +54 -0
- package/src/adapters/duckduckgo/suggest.yaml +52 -0
- package/src/adapters/epo/family.yaml +69 -0
- package/src/adapters/epo/get.yaml +74 -0
- package/src/adapters/epo/legal-status.yaml +63 -0
- package/src/adapters/epo/search.yaml +84 -0
- package/src/adapters/espacenet/_shared.ts +98 -0
- package/src/adapters/espacenet/family.ts +161 -0
- package/src/adapters/espacenet/get.ts +185 -0
- package/src/adapters/espacenet/legal-status.ts +151 -0
- package/src/adapters/espacenet/search.ts +229 -0
- package/src/adapters/facebook/subtitles.ts +44 -0
- package/src/adapters/fips/_shared.ts +109 -0
- package/src/adapters/fips/get.ts +186 -0
- package/src/adapters/fips/search.ts +195 -0
- package/src/adapters/freepatentsonline-web/_shared.ts +273 -0
- package/src/adapters/freepatentsonline-web/get.ts +144 -0
- package/src/adapters/freepatentsonline-web/search.ts +170 -0
- package/src/adapters/google-patents-bq/prior-art.yaml +80 -0
- package/src/adapters/google-patents-bq/search.yaml +97 -0
- package/src/adapters/google-patents-web/_shared.ts +242 -0
- package/src/adapters/google-patents-web/get.ts +224 -0
- package/src/adapters/google-patents-web/search.ts +196 -0
- package/src/adapters/google-scholar/cite.ts +1 -0
- package/src/adapters/google-scholar/profile.ts +5 -0
- package/src/adapters/google-scholar/search.ts +5 -0
- package/src/adapters/hf/paper.test.ts +10 -0
- package/src/adapters/hf/paper.ts +1 -0
- package/src/adapters/hf/top.yaml +1 -1
- package/src/adapters/huggingface-papers/daily.yaml +1 -1
- package/src/adapters/huggingface-papers/search.yaml +1 -1
- package/src/adapters/inpi-br/_shared.ts +98 -0
- package/src/adapters/inpi-br/get.ts +193 -0
- package/src/adapters/inpi-br/search.ts +206 -0
- package/src/adapters/inpi-fr/get.yaml +62 -0
- package/src/adapters/inpi-fr/search.yaml +74 -0
- package/src/adapters/instagram/subtitles.ts +44 -0
- package/src/adapters/ipaustralia/get.yaml +67 -0
- package/src/adapters/ipaustralia/search.yaml +74 -0
- package/src/adapters/jpo/get.yaml +63 -0
- package/src/adapters/jpo/search.yaml +76 -0
- package/src/adapters/kipris/get.yaml +69 -0
- package/src/adapters/kipris/legal-status.yaml +58 -0
- package/src/adapters/kipris/search.yaml +79 -0
- package/src/adapters/lens/get.yaml +64 -0
- package/src/adapters/lens/search.yaml +82 -0
- package/src/adapters/mastodon/statuses.test.ts +82 -0
- package/src/adapters/mastodon/statuses.ts +208 -0
- package/src/adapters/neurips/proceedings.ts +126 -0
- package/src/adapters/openalex/works.ts +33 -0
- package/src/adapters/openreview/papers.ts +5 -0
- package/src/adapters/patsnap/get.yaml +65 -0
- package/src/adapters/patsnap/search.yaml +77 -0
- package/src/adapters/pmlr/proceedings.ts +167 -0
- package/src/adapters/pqai/prior-art.yaml +59 -0
- package/src/adapters/pqai/search.yaml +60 -0
- package/src/adapters/pubmed/articles.ts +5 -0
- package/src/adapters/reddit/comments-tree.test.ts +79 -0
- package/src/adapters/reddit/comments.ts +159 -0
- package/src/adapters/semantic-scholar/papers.ts +268 -0
- package/src/adapters/threads/post.test.ts +64 -0
- package/src/adapters/threads/post.ts +366 -0
- package/src/adapters/threads/user.yaml +73 -0
- package/src/adapters/tiktok/subtitles.ts +44 -0
- package/src/adapters/twitter/accept.ts +5 -2
- package/src/adapters/twitter/browser-fallback.ts +138 -0
- package/src/adapters/twitter/browser-state.ts +74 -0
- package/src/adapters/twitter/client.ts +51 -21
- package/src/adapters/twitter/reply-dm.ts +5 -2
- package/src/adapters/twitter/reply.ts +1 -0
- package/src/adapters/twitter/search.ts +12 -38
- package/src/adapters/twitter/thread.test.ts +43 -0
- package/src/adapters/twitter/thread.ts +44 -2
- package/src/adapters/twitter/trending.ts +14 -95
- package/src/adapters/ukipo/info.yaml +43 -0
- package/src/adapters/unpaywall/works.ts +138 -0
- package/src/adapters/uspto/get.yaml +67 -0
- package/src/adapters/uspto/legal-status.yaml +58 -0
- package/src/adapters/uspto/search.yaml +88 -0
- package/src/adapters/wipo-patentscope/info.yaml +43 -0
- package/src/adapters/xiaohongshu/browser-state.ts +95 -0
- package/src/adapters/xiaohongshu/comments.ts +29 -6
- package/src/adapters/xiaohongshu/download.ts +60 -11
- package/src/adapters/xiaohongshu/search.ts +18 -6
- package/src/adapters/xiaohongshu/trending.ts +112 -0
- package/src/adapters/yahoo/search.yaml +52 -0
- package/src/adapters/youtube/comments-microformat.test.ts +35 -0
- package/src/adapters/youtube/comments-tree.test.ts +74 -0
- package/src/adapters/youtube/comments.ts +166 -12
- package/src/adapters/youtube/subtitles.ts +44 -0
- package/src/adapters/yt-dlp/subtitles.ts +43 -0
- package/src/adapters/zhihu/answer-detail.test.ts +83 -0
- package/src/adapters/zhihu/answer-detail.ts +275 -0
- package/src/adapters/zhihu/comment-tree.test.ts +57 -0
- package/src/adapters/zhihu/comment.ts +186 -0
- package/src/adapters/zhihu/recommend.test.ts +65 -0
- package/src/adapters/zhihu/recommend.ts +207 -0
- package/src/adapters/zotero/search.yaml +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::adapters::unpaywall::works
|
|
3
|
+
* @does Registers Unpaywall DOI open-access lookup for PDF availability.
|
|
4
|
+
* @needs api.unpaywall.org v2, UNPAYWALL_EMAIL or --email, src/registry.ts
|
|
5
|
+
* @feeds src/commands/scholar.ts via scholar.pdf and scholar.get
|
|
6
|
+
* @breaks Missing email is an explicit invalid-input error; Unpaywall drift surfaces as adapter error, never as a fabricated PDF.
|
|
7
|
+
* @invariants Only DOI-shaped references are accepted; best_oa_location is preferred for PDF and landing URLs.
|
|
8
|
+
* @side-effects HTTPS egress to api.unpaywall.org only
|
|
9
|
+
* @perf O(1) per DOI
|
|
10
|
+
* @concurrency safe
|
|
11
|
+
* @test tests/unit/adapters/scholar-sources.test.ts
|
|
12
|
+
* @stability experimental
|
|
13
|
+
* @since 2026-05-19
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { cli, Strategy } from "../../registry.js";
|
|
17
|
+
import type { ScholarlyWorkRecord } from "../../types/scholarly.js";
|
|
18
|
+
|
|
19
|
+
const API = "https://api.unpaywall.org/v2";
|
|
20
|
+
|
|
21
|
+
interface OaLocation {
|
|
22
|
+
url_for_pdf?: unknown;
|
|
23
|
+
url_for_landing_page?: unknown;
|
|
24
|
+
host_type?: unknown;
|
|
25
|
+
version?: unknown;
|
|
26
|
+
license?: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface UnpaywallWork {
|
|
30
|
+
doi?: unknown;
|
|
31
|
+
title?: unknown;
|
|
32
|
+
is_oa?: unknown;
|
|
33
|
+
oa_status?: unknown;
|
|
34
|
+
best_oa_location?: OaLocation | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function str(value: unknown): string {
|
|
38
|
+
return typeof value === "string" ? value.trim() : "";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function bareDoi(value: unknown): string {
|
|
42
|
+
return str(value)
|
|
43
|
+
.replace(/^doi:/i, "")
|
|
44
|
+
.replace(/^https?:\/\/(?:dx\.)?doi\.org\//i, "");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function requireUnpaywallDoi(value: unknown): string {
|
|
48
|
+
const doi = bareDoi(value);
|
|
49
|
+
if (!/^10\.\S+\/\S+/.test(doi)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`unpaywall DOI "${String(value ?? "")}" is not recognised.`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return doi;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function requireEmail(value: unknown): string {
|
|
58
|
+
const email = str(value) || process.env.UNPAYWALL_EMAIL?.trim() || "";
|
|
59
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
60
|
+
throw new Error("unpaywall lookup requires --email or UNPAYWALL_EMAIL.");
|
|
61
|
+
}
|
|
62
|
+
return email;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function mapUnpaywallWork(
|
|
66
|
+
work: UnpaywallWork,
|
|
67
|
+
source: string,
|
|
68
|
+
): ScholarlyWorkRecord {
|
|
69
|
+
const doi = requireUnpaywallDoi(work.doi);
|
|
70
|
+
const best = work.best_oa_location ?? {};
|
|
71
|
+
return {
|
|
72
|
+
id: doi,
|
|
73
|
+
title: str(work.title),
|
|
74
|
+
doi,
|
|
75
|
+
is_open_access: work.is_oa === true,
|
|
76
|
+
oa_status: str(work.oa_status) || undefined,
|
|
77
|
+
pdf_url: str(best.url_for_pdf) || undefined,
|
|
78
|
+
landing_url: str(best.url_for_landing_page) || `https://doi.org/${doi}`,
|
|
79
|
+
type:
|
|
80
|
+
[str(best.host_type), str(best.version), str(best.license)]
|
|
81
|
+
.filter(Boolean)
|
|
82
|
+
.join(":") || undefined,
|
|
83
|
+
source_adapter: source,
|
|
84
|
+
source_url: str(best.url_for_landing_page) || `https://doi.org/${doi}`,
|
|
85
|
+
retrieved_at: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function fetchUnpaywall(
|
|
90
|
+
doi: string,
|
|
91
|
+
email: string,
|
|
92
|
+
): Promise<UnpaywallWork> {
|
|
93
|
+
const response = await fetch(
|
|
94
|
+
`${API}/${encodeURIComponent(doi)}?email=${encodeURIComponent(email)}`,
|
|
95
|
+
{
|
|
96
|
+
headers: {
|
|
97
|
+
Accept: "application/json",
|
|
98
|
+
"User-Agent":
|
|
99
|
+
"unicli-unpaywall/1.0 (https://github.com/olo-dot-io/Uni-CLI)",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
if (response.status === 404)
|
|
104
|
+
throw new Error(`Unpaywall returned no result for ${doi}.`);
|
|
105
|
+
if (response.status === 422)
|
|
106
|
+
throw new Error("Unpaywall rejected the email parameter.");
|
|
107
|
+
if (response.status === 429) throw new Error("Unpaywall returned HTTP 429.");
|
|
108
|
+
if (!response.ok)
|
|
109
|
+
throw new Error(`Unpaywall returned HTTP ${response.status}.`);
|
|
110
|
+
return response.json() as Promise<UnpaywallWork>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
cli({
|
|
114
|
+
site: "unpaywall",
|
|
115
|
+
name: "oa",
|
|
116
|
+
description: "Find open-access PDF availability for a DOI via Unpaywall",
|
|
117
|
+
domain: "api.unpaywall.org",
|
|
118
|
+
strategy: Strategy.PUBLIC,
|
|
119
|
+
args: [
|
|
120
|
+
{ name: "doi", type: "str", required: true, positional: true },
|
|
121
|
+
{ name: "email", type: "str", description: "Unpaywall requester email" },
|
|
122
|
+
],
|
|
123
|
+
columns: [
|
|
124
|
+
"id",
|
|
125
|
+
"title",
|
|
126
|
+
"doi",
|
|
127
|
+
"is_open_access",
|
|
128
|
+
"oa_status",
|
|
129
|
+
"pdf_url",
|
|
130
|
+
"source_url",
|
|
131
|
+
],
|
|
132
|
+
capabilities: ["http.fetch", "scholar.get", "scholar.pdf"],
|
|
133
|
+
func: async (_page, kwargs) => {
|
|
134
|
+
const doi = requireUnpaywallDoi(kwargs.doi ?? kwargs.id ?? kwargs.ref);
|
|
135
|
+
const email = requireEmail(kwargs.email);
|
|
136
|
+
return [mapUnpaywallWork(await fetchUnpaywall(doi, email), "unpaywall")];
|
|
137
|
+
},
|
|
138
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @owner src::adapters::uspto::get
|
|
2
|
+
# @does Fetch a single USPTO patent/application by application number via the ODP detail endpoint.
|
|
3
|
+
# @needs src/engine/steps/fetch.ts, src/engine/steps/map.ts
|
|
4
|
+
# @feeds src/commands/patent.ts
|
|
5
|
+
# @breaks throws PipelineError on 401/403 (missing/invalid X-API-KEY), 404 (number not found) or 5xx
|
|
6
|
+
# @invariants output row conforms to PatentRecord; publication_number is canonicalized to ST.16 form
|
|
7
|
+
# @side-effects reads env USPTO_ODP_API_KEY; HTTPS egress to api.uspto.gov
|
|
8
|
+
# @perf single request
|
|
9
|
+
# @concurrency safe
|
|
10
|
+
# @test none yet
|
|
11
|
+
# @stability stable
|
|
12
|
+
# @since 2026-05-18
|
|
13
|
+
# @verification blocked-by-key — needs USPTO_ODP_API_KEY
|
|
14
|
+
site: uspto
|
|
15
|
+
name: get
|
|
16
|
+
description: Fetch a USPTO patent/application by application number
|
|
17
|
+
domain: api.uspto.gov
|
|
18
|
+
type: web-api
|
|
19
|
+
strategy: public
|
|
20
|
+
lint_listing_detail: skip
|
|
21
|
+
|
|
22
|
+
args:
|
|
23
|
+
application_number:
|
|
24
|
+
type: str
|
|
25
|
+
required: true
|
|
26
|
+
positional: true
|
|
27
|
+
description: USPTO application number (e.g. 16/123,456 with or without slash)
|
|
28
|
+
|
|
29
|
+
pipeline:
|
|
30
|
+
- fetch:
|
|
31
|
+
url: "https://api.uspto.gov/api/v1/patent/applications/${{ args.application_number | replace('/', '') | replace(',', '') }}"
|
|
32
|
+
method: GET
|
|
33
|
+
headers:
|
|
34
|
+
Accept: application/json
|
|
35
|
+
X-API-KEY: "${{ env.USPTO_ODP_API_KEY || '' }}"
|
|
36
|
+
|
|
37
|
+
# ODP single-record detail endpoint returns the same `applicationMetaData`
|
|
38
|
+
# envelope as search, plus a richer `patentTermAdjustment` / `inventor` /
|
|
39
|
+
# `applicant` set on the detail path. Every field reference below maps to a
|
|
40
|
+
# documented ODP path; adapters do not synthesise.
|
|
41
|
+
- map:
|
|
42
|
+
publication_number: "${{ item.applicationMetaData && item.applicationMetaData.earliestPublicationNumber ? ('US-' + item.applicationMetaData.earliestPublicationNumber) : ('US-' + (item.applicationNumberText || '')) }}"
|
|
43
|
+
application_number: "${{ item.applicationNumberText || '' }}"
|
|
44
|
+
title: "${{ item.applicationMetaData ? (item.applicationMetaData.inventionTitle || '') : '' }}"
|
|
45
|
+
abstract: "${{ item.applicationMetaData ? (item.applicationMetaData.abstractText || '') : '' }}"
|
|
46
|
+
filing_date: "${{ item.applicationMetaData ? (item.applicationMetaData.filingDate || '') : '' }}"
|
|
47
|
+
publication_date: "${{ item.applicationMetaData ? (item.applicationMetaData.earliestPublicationDate || '') : '' }}"
|
|
48
|
+
grant_date: "${{ item.applicationMetaData ? (item.applicationMetaData.grantDate || '') : '' }}"
|
|
49
|
+
priority_date: "${{ item.applicationMetaData ? (item.applicationMetaData.firstInventorToFileIndicator ? item.applicationMetaData.filingDate : (item.applicationMetaData.earliestPriorityDate || '')) : '' }}"
|
|
50
|
+
kind_code: "${{ (item.applicationMetaData && item.applicationMetaData.earliestPublicationNumber) ? (item.applicationMetaData.earliestPublicationNumber.match(/[A-Z]\\d?$/) || [''])[0] : '' }}"
|
|
51
|
+
inventors: "${{ JSON.stringify((item.applicationMetaData && item.applicationMetaData.inventorBag) ? item.applicationMetaData.inventorBag.map(p => ({ name: p.inventorNameText || ((p.firstName || '') + ' ' + (p.lastName || '')).trim(), country: p.countryCode || undefined })) : []) }}"
|
|
52
|
+
assignees: "${{ JSON.stringify((item.applicationMetaData && item.applicationMetaData.applicantBag) ? item.applicationMetaData.applicantBag.map(p => ({ name: p.applicantNameText || p.organizationNameText || '', country: p.countryCode || undefined })) : []) }}"
|
|
53
|
+
classifications: "${{ JSON.stringify((item.applicationMetaData && item.applicationMetaData.cpcClassificationBag) ? item.applicationMetaData.cpcClassificationBag.map(c => ({ scheme: 'cpc', code: c.cpcSymbol || c })) : []) }}"
|
|
54
|
+
legal_status: "${{ item.applicationMetaData ? (item.applicationMetaData.applicationStatusDescriptionText || '') : '' }}"
|
|
55
|
+
source_adapter: uspto
|
|
56
|
+
source_url: "${{ 'https://ppubs.uspto.gov/dirsearch-public/patents/' + (item.applicationNumberText || '') }}"
|
|
57
|
+
retrieved_at: ""
|
|
58
|
+
|
|
59
|
+
columns: [publication_number, title, filing_date, publication_date]
|
|
60
|
+
|
|
61
|
+
# schema-v2 metadata
|
|
62
|
+
capabilities: ["http.fetch", "patent.get"]
|
|
63
|
+
minimum_capability: http.fetch
|
|
64
|
+
trust: public
|
|
65
|
+
confidentiality: public
|
|
66
|
+
quarantine: false
|
|
67
|
+
schema_version: v2
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @owner src::adapters::uspto::legal-status
|
|
2
|
+
# @does Fetch USPTO transaction history (PAIR) for an application — used to derive legal-status (issued, abandoned, pending, etc.).
|
|
3
|
+
# @needs src/engine/steps/fetch.ts, src/engine/steps/select.ts, src/engine/steps/map.ts
|
|
4
|
+
# @feeds src/commands/patent.ts
|
|
5
|
+
# @breaks throws PipelineError on 401/403/404
|
|
6
|
+
# @invariants output rows conform to PatentRecord; legal_status carries the latest transaction description
|
|
7
|
+
# @side-effects reads env USPTO_ODP_API_KEY; HTTPS egress to api.uspto.gov
|
|
8
|
+
# @perf single request
|
|
9
|
+
# @concurrency safe
|
|
10
|
+
# @test none yet
|
|
11
|
+
# @stability stable
|
|
12
|
+
# @since 2026-05-18
|
|
13
|
+
# @verification blocked-by-key — needs USPTO_ODP_API_KEY
|
|
14
|
+
site: uspto
|
|
15
|
+
name: legal-status
|
|
16
|
+
description: Legal-status / transaction history of a USPTO application
|
|
17
|
+
domain: api.uspto.gov
|
|
18
|
+
type: web-api
|
|
19
|
+
strategy: public
|
|
20
|
+
lint_listing_detail: skip
|
|
21
|
+
|
|
22
|
+
args:
|
|
23
|
+
application_number:
|
|
24
|
+
type: str
|
|
25
|
+
required: true
|
|
26
|
+
positional: true
|
|
27
|
+
description: USPTO application number
|
|
28
|
+
|
|
29
|
+
pipeline:
|
|
30
|
+
- fetch:
|
|
31
|
+
url: "https://api.uspto.gov/api/v1/patent/applications/${{ args.application_number | replace('/', '') | replace(',', '') }}/transactions"
|
|
32
|
+
method: GET
|
|
33
|
+
headers:
|
|
34
|
+
Accept: application/json
|
|
35
|
+
X-API-KEY: "${{ env.USPTO_ODP_API_KEY || '' }}"
|
|
36
|
+
|
|
37
|
+
- select: eventDataBag
|
|
38
|
+
|
|
39
|
+
# Each row is a transaction event; the ODP `transactions` endpoint returns
|
|
40
|
+
# eventCode + eventDescriptionText + eventDate per transaction.
|
|
41
|
+
- map:
|
|
42
|
+
publication_number: "${{ 'US-' + (args.application_number || '') }}"
|
|
43
|
+
application_number: "${{ args.application_number }}"
|
|
44
|
+
legal_status: "${{ (item.eventCode || '') + (item.eventDescriptionText ? (' ' + item.eventDescriptionText) : '') }}"
|
|
45
|
+
filing_date: "${{ item.eventDate || '' }}"
|
|
46
|
+
source_adapter: uspto
|
|
47
|
+
source_url: "${{ 'https://ppubs.uspto.gov/dirsearch-public/patents/' + (args.application_number || '') }}"
|
|
48
|
+
retrieved_at: ""
|
|
49
|
+
|
|
50
|
+
columns: [publication_number, legal_status, filing_date]
|
|
51
|
+
|
|
52
|
+
# schema-v2 metadata
|
|
53
|
+
capabilities: ["http.fetch", "patent.legal-status"]
|
|
54
|
+
minimum_capability: http.fetch
|
|
55
|
+
trust: public
|
|
56
|
+
confidentiality: public
|
|
57
|
+
quarantine: false
|
|
58
|
+
schema_version: v2
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @owner src::adapters::uspto::search
|
|
2
|
+
# @does Search USPTO patents/applications via the Open Data Portal (ODP) `applications/search` endpoint.
|
|
3
|
+
# @needs src/engine/steps/fetch.ts, src/engine/steps/select.ts, src/engine/steps/map.ts, src/engine/steps/limit.ts
|
|
4
|
+
# @feeds src/commands/patent.ts (meta-command aggregator)
|
|
5
|
+
# @breaks throws PipelineError on 401/403 (missing/invalid X-API-KEY) or 5xx; ODP enforces a 6 MB response cap — narrow the query if hit
|
|
6
|
+
# @invariants output rows conform to PatentRecord; publication_number is canonicalized to ST.16 form `US-<doc>-<kind>`
|
|
7
|
+
# @side-effects reads env USPTO_ODP_API_KEY; HTTPS egress to api.uspto.gov
|
|
8
|
+
# @perf single request, ODP returns up to 500 records per page
|
|
9
|
+
# @concurrency single-call; no shared mutable state
|
|
10
|
+
# @test none yet (adapter ships; live verification gated on key)
|
|
11
|
+
# @stability stable
|
|
12
|
+
# @since 2026-05-18
|
|
13
|
+
# @verification blocked-by-key — needs USPTO_ODP_API_KEY (free tier at developer.uspto.gov)
|
|
14
|
+
site: uspto
|
|
15
|
+
name: search
|
|
16
|
+
description: Search USPTO patents/applications via the Open Data Portal (ODP)
|
|
17
|
+
domain: api.uspto.gov
|
|
18
|
+
type: web-api
|
|
19
|
+
strategy: public
|
|
20
|
+
lint_listing_detail: skip
|
|
21
|
+
|
|
22
|
+
args:
|
|
23
|
+
query:
|
|
24
|
+
type: str
|
|
25
|
+
required: true
|
|
26
|
+
positional: true
|
|
27
|
+
description: Free-text query (mapped to ODP `q` field, matches title/abstract/claims)
|
|
28
|
+
limit:
|
|
29
|
+
type: int
|
|
30
|
+
default: 25
|
|
31
|
+
description: Maximum results to return (ODP allows up to 500 per page)
|
|
32
|
+
offset:
|
|
33
|
+
type: int
|
|
34
|
+
default: 0
|
|
35
|
+
description: Result offset for pagination
|
|
36
|
+
|
|
37
|
+
pipeline:
|
|
38
|
+
- fetch:
|
|
39
|
+
url: "https://api.uspto.gov/api/v1/patent/applications/search"
|
|
40
|
+
method: GET
|
|
41
|
+
headers:
|
|
42
|
+
Accept: application/json
|
|
43
|
+
X-API-KEY: "${{ env.USPTO_ODP_API_KEY || '' }}"
|
|
44
|
+
params:
|
|
45
|
+
q: "${{ args.query }}"
|
|
46
|
+
limit: "${{ args.limit }}"
|
|
47
|
+
offset: "${{ args.offset }}"
|
|
48
|
+
|
|
49
|
+
- select: patentFileWrapperDataBag
|
|
50
|
+
|
|
51
|
+
# Field mapping references data.uspto.gov/swagger v1 patent applications schema.
|
|
52
|
+
# Every field below maps to a documented ODP response path — adapters must
|
|
53
|
+
# not synthesise values upstream did not return.
|
|
54
|
+
- map:
|
|
55
|
+
publication_number: "${{ item.applicationMetaData && item.applicationMetaData.earliestPublicationNumber ? ('US-' + item.applicationMetaData.earliestPublicationNumber) : ('US-' + (item.applicationNumberText || '')) }}"
|
|
56
|
+
application_number: "${{ item.applicationNumberText || '' }}"
|
|
57
|
+
title: "${{ item.applicationMetaData ? (item.applicationMetaData.inventionTitle || '') : '' }}"
|
|
58
|
+
abstract: "${{ item.applicationMetaData ? (item.applicationMetaData.abstractText || '') : '' }}"
|
|
59
|
+
filing_date: "${{ item.applicationMetaData ? (item.applicationMetaData.filingDate || '') : '' }}"
|
|
60
|
+
publication_date: "${{ item.applicationMetaData ? (item.applicationMetaData.earliestPublicationDate || '') : '' }}"
|
|
61
|
+
grant_date: "${{ item.applicationMetaData ? (item.applicationMetaData.grantDate || '') : '' }}"
|
|
62
|
+
# ODP `applicationMetaData.earliestPublicationNumber` is the compact form
|
|
63
|
+
# (`20240123456A1`); the trailing A1/A2/B1/B2 IS the ST.16 kind code.
|
|
64
|
+
kind_code: "${{ (item.applicationMetaData && item.applicationMetaData.earliestPublicationNumber) ? (item.applicationMetaData.earliestPublicationNumber.match(/[A-Z]\\d?$/) || [''])[0] : '' }}"
|
|
65
|
+
# ODP `inventorBag[]` and `applicantBag[]` carry per-party records.
|
|
66
|
+
# We serialise via JSON.stringify so the array survives the template
|
|
67
|
+
# engine's string projection; coerceToPatentRecords parses it back.
|
|
68
|
+
inventors: "${{ JSON.stringify((item.applicationMetaData && item.applicationMetaData.inventorBag) ? item.applicationMetaData.inventorBag.map(p => ({ name: p.inventorNameText || ((p.firstName || '') + ' ' + (p.lastName || '')).trim(), country: p.countryCode || undefined })) : []) }}"
|
|
69
|
+
assignees: "${{ JSON.stringify((item.applicationMetaData && item.applicationMetaData.applicantBag) ? item.applicationMetaData.applicantBag.map(p => ({ name: p.applicantNameText || p.organizationNameText || '', country: p.countryCode || undefined })) : []) }}"
|
|
70
|
+
# `cpcClassificationBag[]` is the ODP path for Cooperative Patent Classification codes.
|
|
71
|
+
classifications: "${{ JSON.stringify((item.applicationMetaData && item.applicationMetaData.cpcClassificationBag) ? item.applicationMetaData.cpcClassificationBag.map(c => ({ scheme: 'cpc', code: c.cpcSymbol || c })) : []) }}"
|
|
72
|
+
legal_status: "${{ item.applicationMetaData ? (item.applicationMetaData.applicationStatusDescriptionText || '') : '' }}"
|
|
73
|
+
source_adapter: uspto
|
|
74
|
+
source_url: "${{ 'https://ppubs.uspto.gov/dirsearch-public/patents/' + (item.applicationNumberText || '') }}"
|
|
75
|
+
# retrieved_at is stamped by assemblePatentRecord in the normalizer; YAML emits the empty string
|
|
76
|
+
retrieved_at: ""
|
|
77
|
+
|
|
78
|
+
- limit: "${{ args.limit }}"
|
|
79
|
+
|
|
80
|
+
columns: [publication_number, title, filing_date, publication_date]
|
|
81
|
+
|
|
82
|
+
# schema-v2 metadata
|
|
83
|
+
capabilities: ["http.fetch", "patent.search"]
|
|
84
|
+
minimum_capability: http.fetch
|
|
85
|
+
trust: public
|
|
86
|
+
confidentiality: public
|
|
87
|
+
quarantine: false
|
|
88
|
+
schema_version: v2
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @owner src::adapters::wipo-patentscope::info
|
|
2
|
+
# @does Registry placeholder for WIPO PATENTSCOPE — surfaces a structured PATENT_API_DEPRECATED envelope so `unicli patent doctor` and `unicli list` advertise the gap, without claiming false coverage.
|
|
3
|
+
# @needs src/engine/steps/assert.ts
|
|
4
|
+
# @feeds src/commands/patent.ts (meta-command discovery via `patent.*` capability)
|
|
5
|
+
# @breaks always throws PipelineError carrying a PATENT_API_DEPRECATED message; never silently returns rows
|
|
6
|
+
# @invariants no HTTPS egress; no env reads; no caller-visible state beyond the error envelope
|
|
7
|
+
# @side-effects none — the assertion is purely local
|
|
8
|
+
# @perf n/a — fails before any I/O
|
|
9
|
+
# @concurrency safe (pure local assertion)
|
|
10
|
+
# @test none yet
|
|
11
|
+
# @stability experimental — replace with a real adapter when WIPO ships a token-based REST/GraphQL API
|
|
12
|
+
# @since 2026-05-18
|
|
13
|
+
# @verification blocked-by-subscription — PATENTSCOPE exposes only a SOAP web service that requires a paid subscription (~CHF 600/year per third-party gem README; see https://patentscope.wipo.int/search/en/sub_user.jsf). Until WIPO ships a token-based REST/GraphQL surface, this adapter is a registry placeholder.
|
|
14
|
+
site: wipo-patentscope
|
|
15
|
+
name: info
|
|
16
|
+
description: WIPO PATENTSCOPE registry placeholder — no public REST API; SOAP requires subscription
|
|
17
|
+
domain: patentscope.wipo.int
|
|
18
|
+
type: web-api
|
|
19
|
+
strategy: public
|
|
20
|
+
lint_listing_detail: skip
|
|
21
|
+
|
|
22
|
+
args: {}
|
|
23
|
+
|
|
24
|
+
pipeline:
|
|
25
|
+
# Fail closed with a structured envelope that the agent can read and act on.
|
|
26
|
+
# The message names the upstream channel and the subscription URL so the
|
|
27
|
+
# caller has a concrete next step — no silent fallback, no fake row.
|
|
28
|
+
- assert:
|
|
29
|
+
condition: "false"
|
|
30
|
+
message: "PATENT_API_DEPRECATED: WIPO PATENTSCOPE exposes only a SOAP web service that requires a paid subscription. Subscribe at https://patentscope.wipo.int/search/en/sub_user.jsf, or fall back to Espacenet for WO-prefixed publications (EPO brokers PCT family lookups)."
|
|
31
|
+
|
|
32
|
+
columns: []
|
|
33
|
+
|
|
34
|
+
# schema-v2 metadata
|
|
35
|
+
# The patent.* capability is what makes `unicli patent doctor` discover this
|
|
36
|
+
# adapter so the gap is reported in the doctor table; assert.fail is the
|
|
37
|
+
# step the dispatcher actually invokes at runtime.
|
|
38
|
+
capabilities: ["assert", "patent.search", "patent.get"]
|
|
39
|
+
minimum_capability: assert
|
|
40
|
+
trust: public
|
|
41
|
+
confidentiality: public
|
|
42
|
+
quarantine: false
|
|
43
|
+
schema_version: v2
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner Xiaohongshu browser adapters.
|
|
3
|
+
* @does Detects login, risk-control, and rendered-feed state in XHS web pages.
|
|
4
|
+
* @needs Browser-backed IPage from Uni-CLI runtime.
|
|
5
|
+
* @feeds xiaohongshu.search and xiaohongshu.trending.
|
|
6
|
+
* @breaks XHS copy or route changes can require updating page-state detection.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { IPage } from "../../types.js";
|
|
10
|
+
import {
|
|
11
|
+
socialAuthError,
|
|
12
|
+
socialChallengeError,
|
|
13
|
+
} from "../../social/browser-errors.js";
|
|
14
|
+
|
|
15
|
+
interface XhsPageState {
|
|
16
|
+
url: string;
|
|
17
|
+
title: string;
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function readXhsPageState(page: IPage): Promise<XhsPageState> {
|
|
22
|
+
const raw = await page.evaluate(`
|
|
23
|
+
(() => ({
|
|
24
|
+
url: window.location.href,
|
|
25
|
+
title: document.title || '',
|
|
26
|
+
text: (document.body?.innerText || '').replace(/\\s+/g, ' ').slice(0, 2000)
|
|
27
|
+
}))()
|
|
28
|
+
`);
|
|
29
|
+
const state = raw as Partial<XhsPageState>;
|
|
30
|
+
return {
|
|
31
|
+
url: String(state.url ?? ""),
|
|
32
|
+
title: String(state.title ?? ""),
|
|
33
|
+
text: String(state.text ?? ""),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function assertXhsReadableState(
|
|
38
|
+
command: string,
|
|
39
|
+
state: XhsPageState,
|
|
40
|
+
): void {
|
|
41
|
+
const haystack = `${state.url}\n${state.title}\n${state.text}`;
|
|
42
|
+
if (
|
|
43
|
+
/website-login\/error|安全限制|IP存在风险|风险|风控|安全验证|验证码|人机验证|verify|captcha/i.test(
|
|
44
|
+
haystack,
|
|
45
|
+
)
|
|
46
|
+
) {
|
|
47
|
+
throw socialChallengeError(
|
|
48
|
+
"xiaohongshu",
|
|
49
|
+
command,
|
|
50
|
+
`Xiaohongshu is showing a risk-control or verification page: ${state.title || state.url}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (/登录后查看|登录后|请先登录|login/i.test(haystack)) {
|
|
54
|
+
throw socialAuthError("xiaohongshu", command);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function assertXhsReadable(
|
|
59
|
+
page: IPage,
|
|
60
|
+
command: string,
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
assertXhsReadableState(command, await readXhsPageState(page));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function fetchXhsFeedItems(page: IPage): Promise<unknown[]> {
|
|
66
|
+
const raw = await page.evaluate(`
|
|
67
|
+
(async () => {
|
|
68
|
+
const app = document.querySelector('#app')?.__vue_app__;
|
|
69
|
+
if (!app) throw new Error('Xiaohongshu Vue app not found');
|
|
70
|
+
const pinia = app.config?.globalProperties?.$pinia;
|
|
71
|
+
if (!pinia || !pinia._s?.has('feed')) throw new Error('Xiaohongshu feed store not found');
|
|
72
|
+
const store = pinia._s.get('feed');
|
|
73
|
+
const captured = [];
|
|
74
|
+
const originalFetch = window.fetch;
|
|
75
|
+
window.fetch = async (...args) => {
|
|
76
|
+
const response = await originalFetch(...args);
|
|
77
|
+
try {
|
|
78
|
+
const url = String(args[0]?.url || args[0] || '');
|
|
79
|
+
if (/homefeed|feed/i.test(url)) {
|
|
80
|
+
captured.push(await response.clone().json());
|
|
81
|
+
}
|
|
82
|
+
} catch {}
|
|
83
|
+
return response;
|
|
84
|
+
};
|
|
85
|
+
try {
|
|
86
|
+
await store.fetchFeeds();
|
|
87
|
+
} finally {
|
|
88
|
+
window.fetch = originalFetch;
|
|
89
|
+
}
|
|
90
|
+
const payload = captured.find((item) => item?.data?.items?.length) || captured[0];
|
|
91
|
+
return payload?.data?.items || [];
|
|
92
|
+
})()
|
|
93
|
+
`);
|
|
94
|
+
return Array.isArray(raw) ? raw : [];
|
|
95
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { cli, Strategy } from "../../registry.js";
|
|
9
9
|
import type { IPage } from "../../types.js";
|
|
10
|
+
import { normalizeCommentRows } from "../../social/comments.js";
|
|
10
11
|
import { buildNoteUrl } from "./note-helpers.js";
|
|
11
12
|
|
|
12
13
|
function parseCommentLimit(raw: unknown, fallback = 20): number {
|
|
@@ -42,7 +43,23 @@ cli({
|
|
|
42
43
|
description: "Include nested replies",
|
|
43
44
|
},
|
|
44
45
|
],
|
|
45
|
-
columns: [
|
|
46
|
+
columns: [
|
|
47
|
+
"rank",
|
|
48
|
+
"platform",
|
|
49
|
+
"content_id",
|
|
50
|
+
"comment_id",
|
|
51
|
+
"parent_id",
|
|
52
|
+
"depth",
|
|
53
|
+
"path",
|
|
54
|
+
"author",
|
|
55
|
+
"text",
|
|
56
|
+
"likes",
|
|
57
|
+
"replies",
|
|
58
|
+
"created",
|
|
59
|
+
"time",
|
|
60
|
+
"is_reply",
|
|
61
|
+
"reply_to",
|
|
62
|
+
],
|
|
46
63
|
func: async (page, kwargs) => {
|
|
47
64
|
const p = page as IPage;
|
|
48
65
|
const limit = parseCommentLimit(kwargs.limit);
|
|
@@ -100,7 +117,7 @@ cli({
|
|
|
100
117
|
const likes = parseLikes(item.querySelector('.count'))
|
|
101
118
|
const time = clean(item.querySelector('.date, .time'))
|
|
102
119
|
if (!text) continue
|
|
103
|
-
results.push({ author, text, likes, time, is_reply: false, reply_to: '' })
|
|
120
|
+
results.push({ author, text, likes, replies: 0, created: time, time, is_reply: false, reply_to: '' })
|
|
104
121
|
if (withReplies) {
|
|
105
122
|
await expandReplyThreads(p)
|
|
106
123
|
p.querySelectorAll('.reply-container .comment-item-sub, .sub-comment-list .comment-item').forEach(sub => {
|
|
@@ -109,7 +126,7 @@ cli({
|
|
|
109
126
|
const sLikes = parseLikes(sub.querySelector('.count'))
|
|
110
127
|
const sTime = clean(sub.querySelector('.date, .time'))
|
|
111
128
|
if (!sText) return
|
|
112
|
-
results.push({ author: sAuthor, text: sText, likes: sLikes, time: sTime, is_reply: true, reply_to: author })
|
|
129
|
+
results.push({ author: sAuthor, text: sText, likes: sLikes, replies: 0, created: sTime, time: sTime, is_reply: true, reply_to: author })
|
|
113
130
|
})
|
|
114
131
|
}
|
|
115
132
|
}
|
|
@@ -125,7 +142,7 @@ cli({
|
|
|
125
142
|
throw new Error("Note comments require login to www.xiaohongshu.com");
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
interface CommentRow {
|
|
145
|
+
interface CommentRow extends Record<string, unknown> {
|
|
129
146
|
author: string;
|
|
130
147
|
text: string;
|
|
131
148
|
likes: number;
|
|
@@ -144,9 +161,15 @@ cli({
|
|
|
144
161
|
if (topCount > limit) break;
|
|
145
162
|
limited.push(c);
|
|
146
163
|
}
|
|
147
|
-
return limited
|
|
164
|
+
return normalizeCommentRows(limited, {
|
|
165
|
+
platform: "xiaohongshu",
|
|
166
|
+
contentId: raw,
|
|
167
|
+
}).map((c, i) => ({ rank: i + 1, ...c }));
|
|
148
168
|
}
|
|
149
169
|
|
|
150
|
-
return all.slice(0, limit)
|
|
170
|
+
return normalizeCommentRows(all.slice(0, limit), {
|
|
171
|
+
platform: "xiaohongshu",
|
|
172
|
+
contentId: raw,
|
|
173
|
+
}).map((c, i) => ({ rank: i + 1, ...c }));
|
|
151
174
|
},
|
|
152
175
|
});
|
|
@@ -7,16 +7,56 @@
|
|
|
7
7
|
|
|
8
8
|
import { cli, Strategy } from "../../registry.js";
|
|
9
9
|
import type { IPage } from "../../types.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
loadCookiesWithCDP,
|
|
12
|
+
formatCookieHeader,
|
|
13
|
+
} from "../../engine/cookies.js";
|
|
14
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { USER_AGENT } from "../../constants.js";
|
|
11
17
|
import { parseNoteId, buildNoteUrl } from "./note-helpers.js";
|
|
12
18
|
|
|
13
19
|
/** Build authenticated headers for Xiaohongshu downloads. */
|
|
14
|
-
function buildCookieHeader(): string {
|
|
15
|
-
const cookies =
|
|
20
|
+
async function buildCookieHeader(): Promise<string> {
|
|
21
|
+
const cookies = await loadCookiesWithCDP(
|
|
22
|
+
"xiaohongshu",
|
|
23
|
+
"www.xiaohongshu.com",
|
|
24
|
+
);
|
|
16
25
|
if (cookies) return formatCookieHeader(cookies);
|
|
17
26
|
return "";
|
|
18
27
|
}
|
|
19
28
|
|
|
29
|
+
function extensionForMedia(type: string, url: string): string {
|
|
30
|
+
try {
|
|
31
|
+
const pathname = new URL(url).pathname;
|
|
32
|
+
const match = /\.[a-zA-Z0-9]{2,5}$/.exec(pathname);
|
|
33
|
+
if (match) return match[0].toLowerCase();
|
|
34
|
+
} catch {
|
|
35
|
+
return type === "video" ? ".mp4" : ".jpg";
|
|
36
|
+
}
|
|
37
|
+
return type === "video" ? ".mp4" : ".jpg";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function downloadMedia(
|
|
41
|
+
url: string,
|
|
42
|
+
path: string,
|
|
43
|
+
cookieHeader: string,
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
const headers: Record<string, string> = {
|
|
46
|
+
"User-Agent": USER_AGENT,
|
|
47
|
+
Referer: "https://www.xiaohongshu.com/",
|
|
48
|
+
};
|
|
49
|
+
if (cookieHeader) headers.Cookie = cookieHeader;
|
|
50
|
+
const response = await fetch(url, { headers });
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Xiaohongshu media download failed: HTTP ${response.status}`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
57
|
+
await writeFile(path, bytes);
|
|
58
|
+
}
|
|
59
|
+
|
|
20
60
|
cli({
|
|
21
61
|
site: "xiaohongshu",
|
|
22
62
|
name: "download",
|
|
@@ -37,11 +77,12 @@ cli({
|
|
|
37
77
|
description: "Output directory",
|
|
38
78
|
},
|
|
39
79
|
],
|
|
40
|
-
columns: ["index", "type", "url"],
|
|
80
|
+
columns: ["index", "type", "path", "url"],
|
|
41
81
|
func: async (page, kwargs) => {
|
|
42
82
|
const p = page as IPage;
|
|
43
83
|
const rawInput = String(kwargs["note-id"]);
|
|
44
84
|
const noteId = parseNoteId(rawInput);
|
|
85
|
+
const outputDir = String(kwargs.output ?? "./xiaohongshu-downloads");
|
|
45
86
|
|
|
46
87
|
await p.goto(buildNoteUrl(rawInput));
|
|
47
88
|
await p.wait(3);
|
|
@@ -158,13 +199,21 @@ cli({
|
|
|
158
199
|
return [{ index: 0, type: "-", url: "No media found" }];
|
|
159
200
|
}
|
|
160
201
|
|
|
161
|
-
|
|
162
|
-
|
|
202
|
+
await mkdir(outputDir, { recursive: true });
|
|
203
|
+
const cookieHeader = await buildCookieHeader();
|
|
163
204
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
205
|
+
const rows = [];
|
|
206
|
+
for (const [idx, media] of data.media.entries()) {
|
|
207
|
+
const ext = extensionForMedia(media.type, media.url);
|
|
208
|
+
const path = join(outputDir, `${noteId}-${idx + 1}${ext}`);
|
|
209
|
+
await downloadMedia(media.url, path, cookieHeader);
|
|
210
|
+
rows.push({
|
|
211
|
+
index: idx + 1,
|
|
212
|
+
type: media.type,
|
|
213
|
+
path,
|
|
214
|
+
url: media.url,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return rows;
|
|
169
218
|
},
|
|
170
219
|
});
|