papersearch 0.4.0 → 0.4.3

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.
Files changed (157) hide show
  1. package/README.en.md +292 -0
  2. package/README.md +203 -122
  3. package/carsi/README.md +9 -3
  4. package/carsi/paper_search_carsi/__init__.py +1 -4
  5. package/carsi/paper_search_carsi/adapters/__init__.py +1 -1
  6. package/carsi/paper_search_carsi/adapters/base.py +2 -0
  7. package/carsi/paper_search_carsi/adapters/frontiers/__init__.py +45 -0
  8. package/carsi/paper_search_carsi/adapters/mdpi/__init__.py +33 -1
  9. package/carsi/paper_search_carsi/adapters/platforms/figshare/__init__.py +185 -0
  10. package/carsi/paper_search_carsi/adapters/thieme/__init__.py +6 -5
  11. package/carsi/paper_search_carsi/browser/__init__.py +8 -1
  12. package/carsi/paper_search_carsi/browser/backend.py +23 -147
  13. package/carsi/paper_search_carsi/browser/challenge_interaction.py +137 -0
  14. package/carsi/paper_search_carsi/browser/challenges.py +57 -0
  15. package/carsi/paper_search_carsi/browser/engine.py +2 -2
  16. package/carsi/paper_search_carsi/browser/preview.py +44 -26
  17. package/carsi/paper_search_carsi/browser/profiles.py +161 -0
  18. package/carsi/paper_search_carsi/browser/watchdog.py +74 -17
  19. package/carsi/paper_search_carsi/config.py +4 -1
  20. package/carsi/paper_search_carsi/download_diagnostics.py +1 -1
  21. package/carsi/paper_search_carsi/engine/batch.py +140 -69
  22. package/carsi/paper_search_carsi/engine/batch_access.py +8 -3
  23. package/carsi/paper_search_carsi/engine/batch_capture.py +54 -9
  24. package/carsi/paper_search_carsi/engine/batch_common.py +27 -14
  25. package/carsi/paper_search_carsi/engine/batch_diagnostics.py +20 -15
  26. package/carsi/paper_search_carsi/engine/batch_login.py +85 -30
  27. package/carsi/paper_search_carsi/engine/batch_runs.py +2 -7
  28. package/carsi/paper_search_carsi/engine/batch_waits.py +97 -47
  29. package/carsi/paper_search_carsi/engine/bridge.py +17 -8
  30. package/carsi/paper_search_carsi/engine/supplements.py +28 -5
  31. package/carsi/paper_search_carsi/entitlements.py +12 -2
  32. package/carsi/paper_search_carsi/log.py +13 -7
  33. package/carsi/paper_search_carsi/orchestrator.py +7 -1
  34. package/carsi/paper_search_carsi/progress_bar.py +156 -12
  35. package/carsi/paper_search_carsi/server.py +8 -1
  36. package/carsi/paper_search_carsi/user_settings.py +55 -0
  37. package/carsi/pyproject.toml +1 -1
  38. package/dist/capabilities/citation-expansion/schemas.d.ts +2 -2
  39. package/dist/capabilities/metadata-search/MultiSourceSearchService.d.ts.map +1 -1
  40. package/dist/capabilities/metadata-search/MultiSourceSearchService.js +17 -4
  41. package/dist/capabilities/metadata-search/MultiSourceSearchService.js.map +1 -1
  42. package/dist/capabilities/metadata-search/handler.d.ts.map +1 -1
  43. package/dist/capabilities/metadata-search/handler.js +6 -0
  44. package/dist/capabilities/metadata-search/handler.js.map +1 -1
  45. package/dist/capabilities/metadata-search/schemas.d.ts +3 -3
  46. package/dist/capabilities/metadata-search/schemas.d.ts.map +1 -1
  47. package/dist/capabilities/metadata-search/schemas.js +1 -0
  48. package/dist/capabilities/metadata-search/schemas.js.map +1 -1
  49. package/dist/capabilities/metadata-search/tools.js +1 -1
  50. package/dist/capabilities/metadata-search/tools.js.map +1 -1
  51. package/dist/capabilities/pdf-discovery/DownloadTier.d.ts +11 -0
  52. package/dist/capabilities/pdf-discovery/DownloadTier.d.ts.map +1 -1
  53. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.d.ts.map +1 -1
  54. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js +31 -1
  55. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js.map +1 -1
  56. package/dist/capabilities/pdf-discovery/arxiv.d.ts +4 -0
  57. package/dist/capabilities/pdf-discovery/arxiv.d.ts.map +1 -0
  58. package/dist/capabilities/pdf-discovery/arxiv.js +42 -0
  59. package/dist/capabilities/pdf-discovery/arxiv.js.map +1 -0
  60. package/dist/capabilities/pdf-discovery/batch.d.ts +1 -1
  61. package/dist/capabilities/pdf-discovery/batch.d.ts.map +1 -1
  62. package/dist/capabilities/pdf-discovery/batch.js +87 -15
  63. package/dist/capabilities/pdf-discovery/batch.js.map +1 -1
  64. package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.d.ts.map +1 -1
  65. package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.js +2 -0
  66. package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.js.map +1 -1
  67. package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.d.ts.map +1 -1
  68. package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.js +3 -3
  69. package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.js.map +1 -1
  70. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.d.ts +2 -1
  71. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.d.ts.map +1 -1
  72. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.js +3 -1
  73. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.js.map +1 -1
  74. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.d.ts +1 -1
  75. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.d.ts.map +1 -1
  76. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.js +4 -4
  77. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.js.map +1 -1
  78. package/dist/capabilities/pdf-discovery/carsi/types.d.ts +2 -0
  79. package/dist/capabilities/pdf-discovery/carsi/types.d.ts.map +1 -1
  80. package/dist/capabilities/pdf-discovery/handler.d.ts.map +1 -1
  81. package/dist/capabilities/pdf-discovery/handler.js +3 -2
  82. package/dist/capabilities/pdf-discovery/handler.js.map +1 -1
  83. package/dist/capabilities/pdf-discovery/oaHosts.d.ts +1 -1
  84. package/dist/capabilities/pdf-discovery/oaHosts.d.ts.map +1 -1
  85. package/dist/capabilities/pdf-discovery/oaHosts.js +4 -4
  86. package/dist/capabilities/pdf-discovery/outputLayout.d.ts +29 -0
  87. package/dist/capabilities/pdf-discovery/outputLayout.d.ts.map +1 -0
  88. package/dist/capabilities/pdf-discovery/outputLayout.js +144 -0
  89. package/dist/capabilities/pdf-discovery/outputLayout.js.map +1 -0
  90. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.d.ts.map +1 -1
  91. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js +6 -0
  92. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js.map +1 -1
  93. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.d.ts +2 -1
  94. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.d.ts.map +1 -1
  95. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.js +8 -1
  96. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.js.map +1 -1
  97. package/dist/capabilities/pdf-discovery/tiers/preprint.d.ts +5 -0
  98. package/dist/capabilities/pdf-discovery/tiers/preprint.d.ts.map +1 -0
  99. package/dist/capabilities/pdf-discovery/tiers/preprint.js +80 -0
  100. package/dist/capabilities/pdf-discovery/tiers/preprint.js.map +1 -0
  101. package/dist/capabilities/pdf-discovery/tiers/primary.js +2 -0
  102. package/dist/capabilities/pdf-discovery/tiers/primary.js.map +1 -1
  103. package/dist/capabilities/pdf-discovery/tiers/unpaywall.d.ts.map +1 -1
  104. package/dist/capabilities/pdf-discovery/tiers/unpaywall.js +12 -3
  105. package/dist/capabilities/pdf-discovery/tiers/unpaywall.js.map +1 -1
  106. package/dist/capabilities/pdf-discovery/types.d.ts +6 -0
  107. package/dist/capabilities/pdf-discovery/types.d.ts.map +1 -1
  108. package/dist/core/handleToolCall.d.ts.map +1 -1
  109. package/dist/core/handleToolCall.js +12 -0
  110. package/dist/core/handleToolCall.js.map +1 -1
  111. package/dist/core/schemas.d.ts +26 -1
  112. package/dist/core/schemas.d.ts.map +1 -1
  113. package/dist/core/schemas.js +13 -0
  114. package/dist/core/schemas.js.map +1 -1
  115. package/dist/core/searchers.d.ts +2 -0
  116. package/dist/core/searchers.d.ts.map +1 -1
  117. package/dist/core/searchers.js.map +1 -1
  118. package/dist/core/tools.d.ts.map +1 -1
  119. package/dist/core/tools.js +30 -0
  120. package/dist/core/tools.js.map +1 -1
  121. package/dist/infrastructure/arxiv.d.ts +5 -0
  122. package/dist/infrastructure/arxiv.d.ts.map +1 -0
  123. package/dist/infrastructure/arxiv.js +36 -0
  124. package/dist/infrastructure/arxiv.js.map +1 -0
  125. package/dist/infrastructure/pdf/PdfDownload.d.ts +2 -0
  126. package/dist/infrastructure/pdf/PdfDownload.d.ts.map +1 -1
  127. package/dist/infrastructure/pdf/PdfDownload.js +14 -0
  128. package/dist/infrastructure/pdf/PdfDownload.js.map +1 -1
  129. package/dist/infrastructure/pdf/PdfFirstPage.cjs +4 -1
  130. package/dist/infrastructure/pdf/PdfFirstPage.cjs.map +1 -1
  131. package/dist/infrastructure/pdf/PdfFirstPage.d.cts.map +1 -1
  132. package/dist/infrastructure/runlog/RunLedger.d.ts.map +1 -1
  133. package/dist/infrastructure/runlog/RunLedger.js +17 -1
  134. package/dist/infrastructure/runlog/RunLedger.js.map +1 -1
  135. package/dist/management/carsi/installer.d.ts +1 -1
  136. package/dist/platforms/ArxivSearcher.d.ts +62 -0
  137. package/dist/platforms/ArxivSearcher.d.ts.map +1 -0
  138. package/dist/platforms/ArxivSearcher.js +544 -0
  139. package/dist/platforms/ArxivSearcher.js.map +1 -0
  140. package/dist/platforms/SemanticScholarSearcher.d.ts.map +1 -1
  141. package/dist/platforms/SemanticScholarSearcher.js +4 -4
  142. package/dist/platforms/SemanticScholarSearcher.js.map +1 -1
  143. package/dist/registry/platformFactories.d.ts.map +1 -1
  144. package/dist/registry/platformFactories.js +2 -0
  145. package/dist/registry/platformFactories.js.map +1 -1
  146. package/dist/registry/platformMetadata.d.ts +1 -1
  147. package/dist/registry/platformMetadata.d.ts.map +1 -1
  148. package/dist/registry/platformMetadata.js +16 -0
  149. package/dist/registry/platformMetadata.js.map +1 -1
  150. package/package.json +2 -2
  151. package/skills/paper-search/SKILL.md +7 -4
  152. package/skills/paper-search/references/browser-download.md +10 -6
  153. package/skills/paper-search/references/capability-routing.md +4 -3
  154. package/skills/paper-search/references/cli-contract.md +10 -3
  155. package/skills/paper-search/references/publisher-support.md +6 -4
  156. package/skills/paper-search/references/troubleshooting.md +1 -1
  157. package/README.zh.md +0 -211
package/README.md CHANGED
@@ -1,41 +1,48 @@
1
1
  # PaperSearch
2
2
 
3
- [简体中文](README.zh.md) | English
3
+ 简体中文 | [English](README.en.md)
4
4
 
5
- PaperSearch is `paper-search`, an academic literature command for AI tools and the terminal, with a bundled Skill that teaches Claude Code, Codex, Cursor and other AI tools how to call it. It searches papers, verifies DOIs and PMIDs, expands citations and references, looks up journal impact factors and quartiles, finds Methods wording in body snippets, and downloads paper PDFs and supplementary files. Output is JSON by default, for AI tools and scripts.
5
+ **让 AI 帮你查文献、核对引用、下载全文和补充材料。**
6
6
 
7
- It is the open version by default: once installed it searches and downloads open-access papers. To download through your school's subscriptions, add the optional institutional download extension.
7
+ PaperSearch 提供 `paper-search` 命令和一份随包 Skill。装好后,Claude Code、Codex、Cursor、Kimi Code、Qwen Code、CodeBuddy、TRAE 等支持本机命令调用的 AI 工具,就能替你检索 20 多个学术数据源、核对 DOI,把论文的 PDF 和补充材料下载下来并整理好。你也可以在终端或脚本里直接使用,输出默认是 JSON。
8
8
 
9
- ![Node.js](https://img.shields.io/badge/node.js->=18.0.0-green.svg)
9
+ - **开放获取直接下载**:PubMed Central、Unpaywall、出版社接口等渠道,装好就能用。
10
+ - **学校订阅也能下**:可选的机构下载扩展经 CARSI 登录你的学校,批量获取学校有权限的论文和补充材料;支持自动过验证,放行后继续下载。
11
+ - **每一篇都核对过**:逐个核对 PDF 首页的 DOI 和题名,不是这篇文章的不算成功。
12
+ - **结果直接能用**:按“期刊_年份_标题”建文件夹,正文和补充材料放在一起;没下到的篇目说明原因。
13
+
14
+ [![npm](https://img.shields.io/npm/v/papersearch)](https://www.npmjs.com/package/papersearch)
15
+ ![Node.js](https://img.shields.io/node/v/papersearch)
10
16
  ![License](https://img.shields.io/badge/license-MIT-blue.svg)
11
- ![Version](https://img.shields.io/badge/version-0.4.0-blue.svg)
12
17
  [![LinuxDo](https://img.shields.io/badge/LinuxDo-community-1f6feb)](https://linux.do)
13
18
 
14
- [How it works](#how-it-works) | [Quick start](#quick-start) | [Two ways to download](#two-ways-to-download) | [Search sources](#search-sources) | [Configuration](#configuration) | [Agent Skill](#agent-skill) | [Commands](#commands) | [Troubleshooting](#troubleshooting)
19
+ [效果](#效果) | [快速开始](#快速开始) | [常见任务](#常见任务) | [工作方式](#工作方式) | [机构下载扩展](#机构下载扩展) | [检索来源](#检索来源) | [配置](#配置) | [Agent Skill](#agent-skill) | [命令](#命令) | [排障](#排障)
20
+
21
+ ## 效果
22
+
23
+ 对 AI 工具说一句话,例如:
15
24
 
16
- ## How it works
25
+ > 找 2020 年以来 SGLT2 抑制剂治疗心力衰竭的随机对照试验,核对 DOI,把全文和补充材料下载到 ./downloads
26
+
27
+ AI 会调用 PaperSearch 检索、核验、下载,最后告诉你哪些拿到了、哪些没拿到以及原因。下面是一次实际下载的结果:
17
28
 
18
29
  ```text
19
- A literature request
20
- │
21
- ├─ Find papers, verify DOI/PMID → paper-search search (Crossref, PubMed, OpenAlex, ...)
22
- │ └─ Entries confirmed, full text needed → go to "Download" below
23
- ├─ Citing papers, references → paper-search run get_paper_citations / get_paper_references
24
- ├─ Impact factor, quartiles → paper-search journal-metrics (EasyScholar key)
25
- ├─ Methods wording, parameters → paper-search run search_semantic_snippets (Semantic Scholar key)
26
- │
27
- └─ Download main text, supplementary files → paper-search download / run download_with_fallback / run download_batch
28
- One download command, tried in order:
29
- ├─ ① Open downloading (built in): the source's own download, PMC, Unpaywall, publisher APIs
30
- ├─ ② Sci-Hub fallback (on by default, can be turned off)
31
- ├─ ③ Institutional download (optional extension): a browser signs in to your school through CARSI and takes the PDF and supplementary files from the publisher's site
32
- ├─ Every file is checked against the DOI and title on its first page; once one passes it is saved and the rest are not tried
33
- └─ Nothing found → paper-search failures says why
30
+ downloads/
31
+ ├── papers/
32
+ │ ├── Nutrients_2026_α-Ketoglutarate Alleviates the Pathogenesis…_0b6e0203de/
33
+ │ │ ├── Nutrients_2026_α-Ketoglutarate Alleviates the Pathogenesis….pdf
34
+ │ │ └── supplements/
35
+ │ │ └── nutrients-18-03600-s001.zip 补充材料保留原文件名
36
+ │ └── arXiv_2017_Attention Is All You Need_a1a0262a54/
37
+ │ └── arXiv_2017_Attention Is All You Need.pdf 预印本会标明
38
+ └── diagnostics/ 运行记录、附件清单、截图
34
39
  ```
35
40
 
36
- ## Quick start
41
+ <!-- 截图:机构下载的进度窗口。图片放在 docs/ 以外(例如 assets/progress-window.png),docs/ 不随公开仓库发布 -->
37
42
 
38
- Requires Node.js 18 or later and npm.
43
+ ## 快速开始
44
+
45
+ 要求 Node.js 18 及以上和 npm。
39
46
 
40
47
  ```bash
41
48
  npm install -g papersearch
@@ -43,168 +50,242 @@ paper-search setup
43
50
  paper-search doctor --format text
44
51
  ```
45
52
 
46
- `paper-search setup` asks three things in turn: API keys and email; which AI tools to install the Skill into (Claude Code, Codex, Cursor, ...); and whether to install the institutional download extension. Each can be skipped. `doctor` shows what works now and what is still missing.
53
+ `paper-search setup` 会依次问三件事:API key 和邮箱;Skill 安装到通用目录还是工具专用目录;要不要安装机构下载扩展。每一步都可以跳过。`doctor` 显示现在能用哪些功能、还缺什么配置。
47
54
 
48
- The Skill is the instruction file that teaches an AI tool to call `paper-search`; install it when you use an AI tool for literature work. If you skipped it, install it any time, with `--targets` set to your tool (`claude`, `codex`, `cursor`, `gemini`, `agents`, or `all`):
55
+ Skill 是教 AI 工具调用 `paper-search` 的说明文件,用 AI 工具做文献任务时要装。跳过了可以随时补装,`--targets` 换成你用的工具(`claude`、`codex`、`cursor`、`gemini`、`agents`,或 `all`):
49
56
 
50
57
  ```bash
51
58
  paper-search skills update --targets claude
52
59
  ```
53
60
 
54
- You can also send this to your AI tool and let it do the install:
61
+ Kimi Code、Qwen Code 使用 `--targets agents`,将 Skill 安装到共享目录。TRAE、CodeBuddy 的接入方式见下方 [Agent Skill](#agent-skill)。
62
+
63
+ 也可以把下面这段话发给你的 AI 工具,让它帮你装:
55
64
 
56
65
  ```text
57
- Please install PaperSearch for me: run npm install -g papersearch, then paper-search skills update --targets <your own target: claude for Claude Code, codex for Codex, cursor for Cursor>, then paper-search doctor --format text, and tell me which features work and which keys are missing. I will enter API keys myself with paper-search setup in a terminal; do not ask me to paste them into the chat.
66
+ 请帮我安装 PaperSearch:运行 npm install -g papersearch,再按 README 的 Agent Skill 说明为你自己安装随包 Skill(Kimi Code、Qwen Code 用 agents,Claude Code 用 claude,Codex 用 codex,Cursor 用 cursor;TRAE、CodeBuddy 使用各自的 Skill 目录),最后运行 paper-search doctor --format text,告诉我哪些功能能用、还缺哪些 key。API key 我会自己在终端运行 paper-search setup 输入,不要让我发到聊天里。
58
67
  ```
59
68
 
60
- **Coming from the old package `paper-search-cli`**: run `npm uninstall -g paper-search-cli`, then `npm install -g papersearch`. Both provide the same `paper-search` command and cannot be installed together; your configuration, Skill, school login and download records stay.
69
+ **从旧包 `paper-search-cli` 升级**:先运行 `npm uninstall -g paper-search-cli`,再 `npm install -g papersearch`。两个包提供同一个 `paper-search` 命令,不能同时装;配置、Skill、学校登录和下载记录都会保留。
61
70
 
62
- **Later upgrades**: `npm install -g papersearch@latest`. The first `paper-search` run after an upgrade brings the Skill you installed up to the new version.
71
+ **以后升级**:`npm install -g papersearch@latest`。升级后第一次运行 `paper-search` 时,它会自动把你装过的 Skill 更新到新版本。
63
72
 
64
- Try the features:
73
+ ## 常见任务
65
74
 
66
75
  ```bash
67
- paper-search search "machine learning clinical prediction" --platform crossref --max-results 3 --pretty
68
- paper-search search "large language models" --sources no-key --max-results 5 --pretty
76
+ # 检索论文(不需要任何 key 的来源组合)
77
+ paper-search search "machine learning clinical prediction" --sources no-key --max-results 5 --pretty
78
+
79
+ # 指定一个来源,例如 PubMed
80
+ paper-search search "SGLT2 inhibitor heart failure" --platform pubmed --max-results 10 --pretty
81
+
82
+ # 核验一个 DOI
83
+ paper-search run get_paper_by_doi --arg doi="10.1038/nature12373" --pretty
84
+
85
+ # 施引文献;参考文献把 get_paper_citations 换成 get_paper_references
69
86
  paper-search run get_paper_citations --arg doi="10.1038/nature12373" --arg limit=5 --pretty
87
+
88
+ # 期刊影响因子和分区(需要 EasyScholar key)
70
89
  paper-search journal-metrics "Nature" "BMJ" --pretty
71
- paper-search download PMC11466435 --platform pmc --save-path ./downloads
90
+
91
+ # 在正文里找 Methods 写法(需要 Semantic Scholar key)
72
92
  paper-search run search_semantic_snippets --arg query="propensity score matching" --arg limit=3 --pretty
93
+
94
+ # 下载一篇
95
+ paper-search download "<DOI>" --platform crossref --save-path ./downloads
96
+
97
+ # 批量下载正文和补充材料(只要正文去掉 content;只要补充材料用 "supplements")
98
+ paper-search run download_batch --json-args '{"items":[{"doi":"<DOI-1>"},{"doi":"<DOI-2>"}],"savePath":"./downloads","content":"both"}'
99
+
100
+ # 没下到的篇目和原因
101
+ paper-search failures --format text
73
102
  ```
74
103
 
75
- ## Two ways to download
104
+ ## 工作方式
76
105
 
77
- | | Open downloading (default) | Institutional download (optional extension) |
106
+ ```text
107
+ 用户的文献请求
108
+ │
109
+ ├─ 找论文、核验 DOI/PMID → paper-search search(Crossref、PubMed、OpenAlex 等)
110
+ │ └─ 确认条目、需要全文 → 转到下面的“下载”
111
+ ├─ 查施引文献、参考文献 → paper-search run get_paper_citations / get_paper_references
112
+ ├─ 查影响因子、分区 → paper-search journal-metrics(需要 EasyScholar key)
113
+ ├─ 找 Methods 写法、参数 → paper-search run search_semantic_snippets(需要 Semantic Scholar key)
114
+ │
115
+ └─ 下载正文、补充材料 → paper-search download / run download_with_fallback / run download_batch
116
+ 同一个下载命令,按顺序尝试:
117
+ ├─ ① 开放下载(默认就有):来源自带的下载、PMC、Unpaywall、出版社接口
118
+ ├─ ② Sci-Hub 兜底(默认开启,可关闭)
119
+ ├─ ③ 机构下载(可选扩展):浏览器经 CARSI 登录你的学校,在出版社网站取 PDF 和补充材料
120
+ ├─ ④ arXiv 预印本:前面都没拿到正式发表版时才用,并标明是预印本
121
+ ├─ 每一步拿到文件都核对首页的 DOI 和题名;通过就保存,后面的步骤不再试
122
+ └─ 都没拿到 → paper-search failures 说明原因
123
+ ```
124
+
125
+ ### 两种下载方式
126
+
127
+ | | 开放下载(默认) | 机构下载(可选扩展) |
78
128
  |---|---|---|
79
- | Available | As soon as PaperSearch is installed | After `paper-search carsi install` |
80
- | Gets | Open-access main text | Main text your school subscribes to; supplementary files from the publishers with rules for them |
81
- | How | Direct downloads, usually seconds, no login: the source's own download, PDF links in the metadata, PubMed Central, Unpaywall, publisher APIs (your own Elsevier/Springer keys); last, the Sci-Hub fallback, on by default | A browser signs in to your school through CARSI and takes the PDF from the publisher's site |
82
- | What you do | Nothing | The first time, type your school account and password in the browser and click any human check; the program picks the school |
129
+ | 能不能直接用 | 装好就能用 | 要另外安装:`paper-search carsi install` |
130
+ | 能拿到什么 | 开放获取的正文 | 你的学校订阅的正文;有规则的出版社还能取补充材料 |
131
+ | 怎么拿 | 程序直接下载,通常几秒,不用登录:来源自带的下载、元数据里的 PDF 地址、PubMed Central、Unpaywall、出版社接口(你自己的 Elsevier/Springer key);最后是默认开启的 Sci-Hub 兜底 | 打开一个浏览器,经 CARSI 登录你的学校,在出版社网站上取 PDF |
132
+ | 你要做的事 | 无 | 在浏览器里登录学校;程序自动过验证,需要人工接管时再按提示操作 |
83
133
 
84
- Both use the same download command: open downloading first, and when it finds nothing, the institutional download if it is installed.
134
+ 两种方式用同一个下载命令:先试开放下载,拿不到时,装了机构下载才继续用学校权限。
85
135
 
86
- ```bash
87
- paper-search download "<DOI>" --platform crossref --save-path ./downloads
88
- paper-search run download_batch --json-args '{"items":[{"doi":"<DOI-1>"},{"doi":"<DOI-2>"}],"savePath":"./downloads"}'
89
- paper-search failures --format text
90
- ```
136
+ - 几十到几百篇用一次 `download_batch`:先对所有篇目试开放渠道,再对没拿到正文的篇目用机构下载。结束后 `paper-search failures` 把没下到的篇目分成四类:没权限(附页面原话等依据)、需要你处理(登录、验证、等待)、暂不支持的网站、原因不明。
137
+ - arXiv 论文先找正式发表版,都没拿到才下载预印本;结果里写 `version: "preprint"`,已知时附正式版 DOI。
138
+ - 不想用 Sci-Hub 时,在 `download_with_fallback` / `download_batch` 里设 `"useSciHub": false`。
139
+ - 每个 PDF 都核对首页的 DOI 和题名;对不上的不算成功,单独留待核查。
140
+
141
+ ### 下载结果
91
142
 
92
- - For dozens to hundreds of papers, use one `download_batch`; afterwards `paper-search failures` sorts the papers not downloaded into four classes: no access (with the evidence, such as the page's own words), needs you (login, human check, waiting), site not supported yet, and cause unknown.
93
- - To leave out Sci-Hub, set `"useSciHub": false` in `download_with_fallback` / `download_batch`.
94
- - Every saved PDF is checked against the DOI and title on its first page; a file of another article is not kept.
143
+ 下载目录分两部分:`papers/` 放论文,每篇一个文件夹,正文按“期刊_年份_标题”命名,补充材料在该篇的 `supplements/` 里并保留原文件名;`diagnostics/` 放运行记录、附件清单和截图,查问题时用。文件夹名末尾的短标识用来区分同名论文。缺少期刊或年份时写“未知期刊”“未知年份”。旧版本下载的文件不会自动搬动,命令返回的 JSON 里写着每个文件的实际位置。
95
144
 
96
- ### Institutional download extension
145
+ ## 机构下载扩展
97
146
 
98
147
  ```bash
99
148
  paper-search carsi install
100
149
  paper-search carsi status --format text
101
150
  ```
102
151
 
103
- - Installing asks for your school (a CARSI member school, by Chinese name, English name or domain) and downloads the dedicated Camoufox browser, about 3 GB in all; on macOS, `--browser ego` uses ego lite instead, about 0.5 GB. `--skip-school` leaves the school for later.
104
- - Schools sign in through CARSI (the Chinese university identity federation). When a publisher's institution list has no CARSI or no entry for your school, the program says so at once; download on campus or through your school's VPN instead.
105
- - `paper-search carsi update` updates the extension after a PaperSearch upgrade; `paper-search carsi uninstall` removes it (keeping the browser and the school settings by default).
106
- - Windows: if PowerShell blocks `npm.ps1`, use `npm.cmd` or Command Prompt; downloads must run in a desktop session, as a browser window cannot open from SSH or a system service.
152
+ - 安装时会问你的学校(CARSI 成员学校,中文名、英文名或域名都可以),并下载专用浏览器 Camoufox,共约 3 GB;macOS 上 `--browser ego` 改用 ego lite,约 0.5 GB。`--skip-school` 可以以后再选学校。
153
+ - 目前用 CARSI(中国高校身份联盟)登录学校;出版社的机构列表里没有 CARSI 或你的学校时,程序会立即说明,建议在校园网或学校 VPN 下下载。
154
+ - 自动过验证:程序自动定位并点击验证框,网站放行后继续下载。每次验证等待最多尝试 3 次,两次至少间隔 3 秒;自动阶段结束后仍未放行时,进度窗口会提示人工接管。
155
+ - 下载时会出现进度窗口:可以跳过本篇、标记本刊或出版社无权限、终止整个任务,右上角齿轮可以调整等待时间。停在学校登录页默认最多等 3 分钟,停在其他页面(人机验证、出版社页面等)默认 1 分钟后结束这一篇。
156
+ - 能取哪些出版社的正文和补充材料,见 [出版社支持表](skills/paper-search/references/publisher-support.md)。正文和补充材料都实测取得的有 Elsevier、Wiley、Springer Nature、OUP、Wolters Kluwer、Taylor & Francis、Sage、ACS、NEJM、ASCE、Dove 等,另支持 MDPI、Frontiers 的补充材料和论文关联的 Figshare 附件;IEEE、Thieme 等目前只取正文;表里没有的网站按通用规则尝试。
157
+ - `paper-search carsi update` 在 PaperSearch 升级后更新扩展,`paper-search carsi uninstall` 卸载(默认保留浏览器和学校配置)。
158
+ - Windows:PowerShell 拦住 `npm.ps1` 时改用 `npm.cmd` 或命令提示符;下载要在桌面会话里运行,经 SSH 或系统服务启动时浏览器窗口打不开。
107
159
 
108
- ### Supplementary files
160
+ ## 检索来源
109
161
 
110
- `content` in `download_with_fallback` / `download_batch` picks what to download: `main` (default, main text only), `both` (main text and supplementary files), or `supplements` (supplementary files only). Supplementary files come only through the institutional download; they are saved under `<save path>/<DOI>_supplements/`, with a `manifest.json` giving each file's status.
162
+ | 类别 | 来源 | 需要的 API key |
163
+ |---|---|---|
164
+ | 综合学术元数据 | Crossref、OpenAlex、Semantic Scholar、Google Scholar | 不需要(Semantic Scholar 的正文片段检索需要) |
165
+ | 生物医学 | PubMed、PubMed Central、Europe PMC | 不需要(PubMed key 可提高限额) |
166
+ | 预印本和会议稿 | arXiv、bioRxiv、medRxiv、OpenReview、IACR ePrint | 不需要 |
167
+ | 计算机和工程 | DBLP、ACM、USENIX | 不需要 |
168
+ | 开放获取和仓储 | CORE、OpenAIRE、Unpaywall | Unpaywall 填邮箱;CORE、OpenAIRE 可选 |
169
+ | 出版社和引文库 | ScienceDirect、Scopus、Springer Nature、IEEE Xplore、Web of Science | 需要各自的 key(ScienceDirect、Scopus 共用 Elsevier key) |
170
+ | 期刊指标 | EasyScholar | 需要 |
111
171
 
112
- What each publisher supports, main text and supplementary files, is in the [publisher support table](skills/paper-search/references/publisher-support.md) (Chinese). Main text and supplementary files have both been fetched from Elsevier, Wiley, Springer Nature, OUP, Wolters Kluwer, Taylor & Francis, Sage, ACS, NEJM, ASCE, Dove and others; IEEE, Thieme and others give the main text only for now; sites not in the table are tried with the shared rules.
172
+ - 不需要 key 的来源已经收录了各大出版社的论文;配了 key,多的是出版社自己的检索入口。
173
+ - 单个来源用 `--platform`,多个用 `--sources a,b,c`;`--sources no-key` 是不需要任何 key 的组合(Crossref、PubMed、Europe PMC、arXiv、OpenReview)。
174
+ - Google Scholar 是网页解析,容易触发验证,不在 `--sources all` 里;需要时用 `--platform googlescholar`。
175
+ - 多个来源返回同一篇论文时按 DOI 合并;arXiv 预印本和正式发表版有明确 DOI 关联时,保留正式发表记录。
176
+ - Sci-Hub 不是检索来源,只在下载时按 DOI 兜底。
113
177
 
114
- ## Search sources
178
+ ## 配置
115
179
 
116
- | Kind | Sources | Configuration |
117
- |---|---|---|
118
- | General academic metadata | Crossref, OpenAlex, Semantic Scholar, Google Scholar | None; Semantic Scholar body snippets need a key; Google Scholar must be asked for by name |
119
- | Biomedicine | PubMed, PubMed Central, Europe PMC | Optional PubMed key |
120
- | Preprints and conference papers | bioRxiv, medRxiv, OpenReview, IACR ePrint | None |
121
- | Computer science and engineering | DBLP, ACM, USENIX, IEEE Xplore | IEEE needs a key |
122
- | Open access and repositories | CORE, OpenAIRE, Unpaywall | Unpaywall needs an email; CORE key optional |
123
- | Citation databases and publishers | Web of Science, Scopus, ScienceDirect, Springer Nature | Each needs its key; metadata only (Elsevier and Springer keys also serve the publisher API for full text) |
124
- | Journal metrics | EasyScholar | EasyScholar key |
180
+ PaperSearch 有两套配置,各管各的,都可以不配:
125
181
 
126
- - One source with `--platform`, several with `--sources a,b,c`; `--sources no-key` needs no keys at all (Crossref, PubMed, Europe PMC, OpenReview).
127
- - Google Scholar is read from its web pages and often meets human checks, so it is not in `--sources all`; use `--platform googlescholar` when you need it.
128
- - Sci-Hub is not a search source; it only serves as a DOI fallback when downloading.
182
+ | | API key | CARSI(机构下载扩展) |
183
+ |---|---|---|
184
+ | 用来做什么 | 部分来源的检索、期刊指标、正文片段检索;Elsevier、Springer 的 key 还能经出版社接口取全文 | 登录你的学校,按学校订阅下载全文和补充材料 |
185
+ | 怎么配 | `paper-search setup`,或 `paper-search config set` | `paper-search carsi install`,安装时选学校 |
186
+ | 不配时 | 用免费来源检索;“检索来源”里需要 key 的来源和功能不可用 | 下载只走开放下载渠道(见[两种下载方式](#两种下载方式)) |
129
187
 
130
- ## Configuration
188
+ ### API key 与配置文件
131
189
 
132
- Most free sources need nothing. Run `paper-search setup` first to save keys in your user configuration:
190
+ 多数免费来源不需要配置。建议先运行 `paper-search setup`,把 key 写入用户配置:
133
191
 
134
192
  ```bash
135
193
  paper-search setup
136
194
  paper-search config list --pretty
137
- paper-search config set SEMANTIC_SCHOLAR_API_KEY <your key>
195
+ paper-search config set SEMANTIC_SCHOLAR_API_KEY <你的 key>
138
196
  ```
139
197
 
140
- The configuration file is `~/.config/paper-search-cli/config.json` (mode `0600`; the folder keeps its old name, so upgrades keep your settings). `config list` and `doctor` show keys masked.
198
+ 配置文件在 `~/.config/paper-search-cli/config.json`(权限 `0600`;文件夹沿用旧名,升级不丢设置),`config list` 和 `doctor` 会把 key 脱敏显示。
141
199
 
142
- | Key | Use | When |
200
+ | 配置项 | 用途 | 什么时候配置 |
143
201
  |---|---|---|
144
- | `SEMANTIC_SCHOLAR_API_KEY` | Body snippet search; steadier Semantic Scholar requests | For Methods details, or frequent Semantic Scholar use |
145
- | `UNPAYWALL_EMAIL` | Open-access PDFs by DOI | Recommended; an email is enough |
146
- | `CROSSREF_MAILTO` | Crossref's polite pool | Long or frequent searches |
147
- | `CORE_API_KEY` | CORE repository search | Frequent CORE use, or rate limits |
148
- | `EASYSCHOLAR_KEY` | Journal metrics | For impact factors and quartiles; `paper-search setup EASYSCHOLAR_KEY` hides the input |
149
- | `PUBMED_API_KEY`, `NCBI_EMAIL` | Higher PubMed limits | Frequent PubMed use |
150
- | `WOS_API_KEY`, `IEEE_API_KEY`, `ELSEVIER_API_KEY`, `SPRINGER_API_KEY` | Their metadata; Elsevier and Springer keys also serve the publisher API for full text (access follows your network or key) | When you use those sources |
151
- | `ELSEVIER_INSTTOKEN` | Elsevier full text off campus through its API | Off campus; not needed on a campus network |
152
- | `GOOGLE_SCHOLAR_PROXY_URL` | Egress proxy for Google Scholar | When direct requests keep being challenged; use a proxy you are allowed to use |
153
- | `PAPER_SEARCH_CARSI_ENABLED` | Switch for the institutional download extension | Set by `paper-search carsi install` |
154
- | `PAPER_SEARCH_SKILL_AUTO_UPDATE` | `false` stops upgrades from updating the installed Skill | On by default; set it to manage the Skill yourself |
155
-
156
- Where to get keys: [EasyScholar](https://www.easyscholar.cc/console/user/open) · [Semantic Scholar](https://www.semanticscholar.org/product/api) · [Unpaywall](https://unpaywall.org/products/api) · [CORE](https://core.ac.uk/services/api) · [NCBI](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) · [Web of Science](https://developer.clarivate.com/apis) · [IEEE Xplore](https://developer.ieee.org/docs/read/Searching_the_IEEE_Xplore_Metadata_API) · [Elsevier](https://dev.elsevier.com/apikey/manage) · [Springer Nature](https://dev.springernature.com/) · [OpenAIRE](https://develop.openaire.eu/)
202
+ | `SEMANTIC_SCHOLAR_API_KEY` | 正文片段检索,更稳定的 Semantic Scholar 请求 | 要查方法细节,或经常用 Semantic Scholar |
203
+ | `UNPAYWALL_EMAIL` | 按 DOI 找开放获取 PDF | 推荐配置;填邮箱即可 |
204
+ | `CROSSREF_MAILTO` | Crossref 优先通道 | 长任务或高频检索 |
205
+ | `CORE_API_KEY` | CORE 仓储检索 | 经常用 CORE 或遇到限流 |
206
+ | `EASYSCHOLAR_KEY` | 期刊指标 | 要查影响因子、分区;用 `paper-search setup EASYSCHOLAR_KEY` 隐藏输入 |
207
+ | `PUBMED_API_KEY`、`NCBI_EMAIL` | PubMed 更高限额 | 经常用 PubMed |
208
+ | `WOS_API_KEY`、`IEEE_API_KEY`、`ELSEVIER_API_KEY`、`SPRINGER_API_KEY` | 各自的元数据;Elsevier、Springer 的 key 也用于出版社接口取全文(权限跟随你的网络或 key) | 要用对应来源时 |
209
+ | `ELSEVIER_INSTTOKEN` | 校外经 Elsevier 接口取全文 | 校外需要全文时;校园网不需要 |
210
+ | `GOOGLE_SCHOLAR_PROXY_URL` | Google Scholar 的出口代理 | 直连反复被拦时,用有授权的代理 |
211
+ | `PAPER_SEARCH_CARSI_ENABLED` | 机构下载扩展的开关 | `paper-search carsi install` 自动设置 |
212
+ | `PAPER_SEARCH_SKILL_AUTO_UPDATE` | 设为 `false` 时,升级后不再自动更新已安装的 Skill | 默认开启,想自己管理 Skill 时设置 |
213
+
214
+ 申请入口:[EasyScholar](https://www.easyscholar.cc/console/user/open) · [Semantic Scholar](https://www.semanticscholar.org/product/api) · [Unpaywall](https://unpaywall.org/products/api) · [CORE](https://core.ac.uk/services/api) · [NCBI](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) · [Web of Science](https://developer.clarivate.com/apis) · [IEEE Xplore](https://developer.ieee.org/docs/read/Searching_the_IEEE_Xplore_Metadata_API) · [Elsevier](https://dev.elsevier.com/apikey/manage) · [Springer Nature](https://dev.springernature.com/) · [OpenAIRE](https://develop.openaire.eu/)
215
+
216
+ ### CARSI
217
+
218
+ 安装和使用见 [机构下载扩展](#机构下载扩展)。它不用任何 API key,也不提供检索。
157
219
 
158
220
  ## Agent Skill
159
221
 
160
- The Skill ships in the npm package (`skills/paper-search/`). It is written in Chinese, opens with the same overall diagram, and points the AI to focused references as needed: routing and the open download channels, the command contract, installing and using the institutional download, the [publisher support table](skills/paper-search/references/publisher-support.md), and troubleshooting. It only tells the AI how to call `paper-search`; it keeps no keys, cookies or accounts.
222
+ Skill 随 npm 包发布(`skills/paper-search/`),全中文,开头有整体逻辑图,按需读这些参考文件:功能路由与开放下载的各个渠道、命令约定、机构下载的安装和使用、[出版社支持表](skills/paper-search/references/publisher-support.md)、排障。它只告诉 AI 怎么调用 `paper-search`,不保存 key、Cookie 或账号。
161
223
 
162
224
  ```bash
163
225
  paper-search skills status --pretty
164
226
  paper-search skills update --targets claude
165
227
  ```
166
228
 
167
- - Targets: `agents`, `codex`, `claude`, `cursor`, `gemini`, `antigravity`, `all`. Updating overwrites the files the package manages and keeps files you added to the Skill directory.
168
- - **Auto-update after upgrades**: the first `paper-search` run after an upgrade (other than `skills` and `setup`) brings the Skill you installed up to the new version: only where it was installed; replaced files are backed up under `~/.local/share/paper-search-cli/skill-backups/`; one line on stderr says what changed. With no Skill installed anywhere, it says once how to install it. To turn it off: `paper-search config set PAPER_SEARCH_SKILL_AUTO_UPDATE false`.
169
- - `paper-search doctor` shows where the Skill is installed and whether it is current.
229
+ 国内 AI 工具可以按以下方式加载同一份 Skill:
230
+
231
+ | 工具 | 接入方式 |
232
+ |---|---|
233
+ | [Kimi Code](https://www.kimi.com/code/docs/kimi-code-cli/customization/skills.html)、[Qwen Code](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/skills.md) | 运行 `paper-search skills update --targets agents`,安装到它们共同支持的 `~/.agents/skills/paper-search/` |
234
+ | [TRAE](https://docs.trae.ai/ide/skills?_lang=zh) | 将随包 Skill 文件夹复制到项目的 `.trae/skills/paper-search/` |
235
+ | [CodeBuddy](https://www.codebuddy.ai/docs/zh/ide/Features/Skills) | 将随包 Skill 文件夹复制到项目的 `.codebuddy/skills/paper-search/` |
236
+
237
+ `npm root -g` 可查看全局 npm 目录;随包 Skill 位于其下的 `papersearch/skills/paper-search/`。复制时保留完整文件夹及 `references/`,更新时从新版本复制。安装后新开 AI 会话即可使用。
238
+
239
+ - 支持的目标:`agents`、`codex`、`claude`、`cursor`、`gemini`、`antigravity`、`all`。更新会覆盖包里管理的文件,保留你在 Skill 目录里自己加的文件。
240
+ - **升级后自动更新**:通过内置目标安装的 Skill,会在升级 PaperSearch 后第一次运行 `paper-search`(`skills`、`setup` 除外)时更新到新版本:只更新原来装过的位置;被替换的旧文件备份在 `~/.local/share/paper-search-cli/skill-backups/`;并在 stderr 写一行说明。一个都没装时只提示一次怎么装。不想自动更新:`paper-search config set PAPER_SEARCH_SKILL_AUTO_UPDATE false`。
241
+ - `paper-search doctor` 显示内置安装目标中的 Skill 位置和版本状态。
170
242
 
171
- ## Commands
243
+ ## 命令
172
244
 
173
- | Command | Use |
245
+ | 命令 | 用途 |
174
246
  |---|---|
175
- | `paper-search search` | Search paper metadata |
176
- | `paper-search journal-metrics` | Journal metrics (EasyScholar) |
177
- | `paper-search download` | Download the PDF of a verified paper |
178
- | `paper-search run <tool>` | Call one tool exactly, with `--arg` or `--json-args` |
179
- | `paper-search failures` | Why downloads failed, summarized |
180
- | `paper-search carsi` | Install, check, update or remove the institutional download extension |
181
- | `paper-search setup` / `config` | Configure keys, the Skill and the extension |
182
- | `paper-search doctor` | Health check: configuration, available features, Skill and extension status |
183
- | `paper-search skills` | Skill status, differences and updates |
184
- | `paper-search smoke` | Offline or live self-check |
185
- | `paper-search tools` | All tool names and their arguments |
186
-
187
- Commands print JSON by default; `--pretty` formats it and `--format text` prints a report for people. All commands: `paper-search tools --pretty`, or the [command contract](skills/paper-search/references/cli-contract.md) (Chinese).
188
-
189
- ## Troubleshooting
190
-
191
- | Problem | What to do |
247
+ | `paper-search search` | 检索论文元数据 |
248
+ | `paper-search journal-metrics` | 期刊指标(EasyScholar) |
249
+ | `paper-search download` | 下载已核验论文的 PDF |
250
+ | `paper-search run <工具>` | 精确调用某个工具,参数用 `--arg` 或 `--json-args` |
251
+ | `paper-search failures` | 汇总下载失败的原因 |
252
+ | `paper-search carsi` | 机构下载扩展的安装、状态、更新、卸载 |
253
+ | `paper-search setup` / `config` | 配置 key、Skill 和扩展 |
254
+ | `paper-search doctor` | 健康检查:配置、可用功能、Skill 和扩展的状态 |
255
+ | `paper-search skills` | Skill 的状态、差异和更新 |
256
+ | `paper-search smoke` | 离线或真实的自检 |
257
+ | `paper-search tools` | 列出所有工具名和参数 |
258
+
259
+ 命令默认输出 JSON,`--pretty` 排版,`--format text` 输出给人看的报告。完整命令见 `paper-search tools --pretty` 或 [命令约定](skills/paper-search/references/cli-contract.md)。
260
+
261
+ ## 排障
262
+
263
+ | 问题 | 怎么办 |
192
264
  |---|---|
193
- | `paper-search` not found | Reinstall with `npm install -g papersearch` |
194
- | `EEXIST` while installing | The old package is still there: `npm uninstall -g paper-search-cli` first |
195
- | A feature does not work | `paper-search doctor --format text` shows what is missing; then `paper-search setup` |
196
- | Subscribed articles do not download | `paper-search carsi status --format text` for the extension; `paper-search failures --format text` for each paper's reason |
197
- | A source rate-limits | Lower `--max-results`, configure its key, or switch sources |
198
- | Google Scholar shows a human check | Wait and retry, or use `--sources no-key` |
199
- | The AI reads an old Skill | `paper-search skills status --pretty`, then `paper-search skills update --targets <tool>`; start a new AI session |
265
+ | 找不到 `paper-search` 命令 | `npm install -g papersearch` 重新安装 |
266
+ | 安装时报 `EEXIST` | 旧包还在:先 `npm uninstall -g paper-search-cli` |
267
+ | 某个功能不能用 | `paper-search doctor --format text` 看缺什么,再 `paper-search setup` 配置 |
268
+ | 学校订阅的文章下载不到 | `paper-search carsi status --format text` 看扩展是否装好;`paper-search failures --format text` 看每篇的原因 |
269
+ | 来源限流 | 减小 `--max-results`、配置对应 key,或换来源 |
270
+ | Google Scholar 出验证页 | 等一会儿再试,或改用 `--sources no-key` |
271
+ | AI 读到的 Skill 是旧的 | `paper-search skills status --pretty`,再 `paper-search skills update --targets <工具>`;新开一个 AI 会话 |
272
+
273
+ ## 路线图与参与
274
+
275
+ - 接下来的重点:批量下载更稳、新电脑安装更顺;在 PDF 之外交付全文包(原始 XML/HTML、Markdown 和正文图片)。详见 [路线图](docs/ROADMAP.zh.md)。
276
+ - 想了解原理和代码结构:[架构说明](docs/ARCHITECTURE.zh.md)。
277
+ - 下载失败或想要支持某家出版社:在 Issues 里附上 `paper-search failures --format text` 的输出(先删掉不想公开的信息)。
278
+ - 想自己加出版社规则:见 [carsi/CONTRIBUTING.md](carsi/CONTRIBUTING.md)。
200
279
 
201
- ## Use responsibly
280
+ ## 使用边界
202
281
 
203
- Some sources are subject to platform terms, institutional subscriptions or local law; use them only where you have access. The institutional download uses your own school account and subscriptions; the source labels in results are for diagnosis and are not proof of entitlement.
282
+ 部分来源受平台条款、机构订阅或当地法律约束,只在你有相应访问权时使用。机构下载用的是你自己的学校账号和订阅;结果里的来源标记只用于诊断,不能当作授权证明。
204
283
 
205
- ## Origins
284
+ ## 项目来源
206
285
 
207
- Thanks to the [LinuxDo](https://linux.do) community, where the command-plus-Skill approach and workflow improvements came from discussion and open sharing. The project draws on ideas from [openags/paper-search-mcp](https://github.com/openags/paper-search-mcp), reworked into a standalone command; the institutional download backend was carved out of scansci-pdf, see `carsi/NOTICE`.
286
+ - 感谢 [LinuxDo](https://linux.do) 社区:命令 + Skill 的路线和工作流改进来自社区交流与开源分享。
287
+ - [openags/paper-search-mcp](https://github.com/openags/paper-search-mcp):本项目参考了它的思路,并改成独立的命令行工具。
288
+ - [Rimagination/scansci-pdf](https://github.com/Rimagination/scansci-pdf)(MIT):机构下载后端(`carsi/`)借鉴了它的架构(CARSI 等联盟登录、出版社网站抓取、会话复用、PDF 捕获);原项目的版权声明保留在 `carsi/NOTICE`。
208
289
 
209
290
  ## License
210
291
 
package/carsi/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # paper-search-carsi
2
2
 
3
- PaperSearch 的机构授权 PDF 下载后端,从 scansci-pdf 裁出,只保留"机构访问"这一段:CARSI 等联盟入口、出版社浏览器抓取、会话复用、PDF 捕获。公开/OA 来源、灰色来源、搜索、批量、Tor、Zotero 都不在这里,那些由 PaperSearch 自己负责。WebVPN、EZProxy(经学校的网关或代理用学校 IP)也不在这里:代码暂存在仓库根目录 `campus-proxy/`,未接入。
3
+ PaperSearch 的机构授权下载后端,负责 CARSI 等联盟登录、出版社页面交互、会话复用、正文 PDF 与补充材料获取。公开来源、检索和批量编排由 PaperSearch CLI 负责。WebVPN、EZProxy(经学校的网关或代理用学校 IP)也不在这里:代码暂存在仓库根目录 `campus-proxy/`,未接入。
4
4
 
5
5
  状态:**MCP server、编排和 CLI 已接通,PaperSearch `doctor` 握手通过;真实机构下载尚待逐出版社验收。**
6
6
 
@@ -23,7 +23,9 @@ python3 -m venv .venv
23
23
 
24
24
  ## 使用
25
25
 
26
- 等待用户操作(学校登录、人机验证)每篇累计最多 5 分钟,从第一次等待算起(配置键 `browser_login_timeout`,秒,默认 300)。超时后结果为 `login_timeout`(学校登录)或 `download_timeout`(人机验证)。工具参数 `user_wait: "short"`(PaperSearch 批量里有一篇等满时限后传给后面的篇目)表示用户多半不在:每次等待最多 60 秒,登录页 60 秒没有变化也结束,登录框里有人输入则照常等。
26
+ 遇到 Cloudflare/Turnstile 验证框时,程序先等待网站自动检查,再自动定位并点击复选框,网站放行后继续下载。每次验证等待最多尝试 3 次,两次至少间隔 3 秒,复用当前浏览器和原等待时限;自动阶段结束后仍未放行时提示人工接管。学校登录及二次验证由本人在浏览器完成。浏览器兼容设置随机构下载扩展安装,在新电脑上同样生效。
27
+
28
+ 等待用户操作:停在学校登录页(以及没配置学校时的选校页)每篇最多 3 分钟(配置键 `browser_login_timeout`,秒,默认 180);停在其他页面(人机验证、出版社页面、选机构页、取 PDF 时等待处理)连续超过 1 分钟就结束这一篇(`browser_page_wait_timeout`,默认 60)。两项和每家出版社每小时最多下载篇数都能在进度窗口右上角的设置(齿轮)里改。浏览器某一步 90 秒没有响应时,这一篇以 `browser_unresponsive` 结束。进度窗口的按钮:跳过本篇、本刊无权限、出版社无权限、终止任务(整批停下,剩余篇目记为 `batch_stopped`)、⚑ 报告问题。
27
29
 
28
30
  浏览器调用超过 4 分钟没有返回时,后端会取消它并把这一篇记为 `browser_unresponsive`(日志里记下是哪个调用、从哪里调用),批量继续下一篇。后端一直停着不动(CPU 为 0、进度不再更新)时,`kill -USR1 <后端进程号>` 会把它所有线程的 Python 调用栈追加到 `<数据目录>/logs/stacks-<日期>.txt`,用来看它在等哪个浏览器调用;进程不会因此退出。
29
31
 
@@ -53,6 +55,8 @@ paper-search-carsi publisher-check --doi 10.1016/...
53
55
 
54
56
  ## 三级结构
55
57
 
58
+ 目录保留两层外壳:`carsi/` 是后端构建根(安装声明、测试和工具),`paper_search_carsi/` 是可导入、可安装的 Python 包。出版社各自保留独立文件夹,便于增加网站类型和专有规则;不因暂时只有一个 `__init__.py` 就改成单文件。目录是否精简按实际职责和明确扩展用途判断,核对记录见 `目录层级核对`。
59
+
56
60
  ```text
57
61
  paper_search_carsi/
58
62
  ├── publishers/ tier 1 声明式出版社规则:builtin/*.json + schema.json + 用户 publisher_profiles_file
@@ -72,4 +76,6 @@ paper_search_carsi/
72
76
  | tier 2 `adapters/<name>/` | Python,`PublisherAdapter` 子类 | 离线夹具测试;`publisher-check` 报告贴在 PR |
73
77
  | tier 3 `engine/` | 熟悉学校认证流程 | 维护者审核 |
74
78
 
75
- 详见 CONTRIBUTING.md。
79
+ 详见 CONTRIBUTING.md。后端在契约之外的行为(浏览器预览、出版社规则扩展文件)见仓库的 `docs/dependencies/carsi-backend-notes.zh.md`。当前模块职责见同一后端说明中的“浏览器内部模块边界”。
80
+
81
+ PaperSearch 调用后端时可以传入独立的 `diagnostics_dir`:浏览器记录、截图和附件清单放在该目录;正文及 `supplements/` 附件仍写入 `output_dir`。期刊、年份、标题命名由 CLI 的统一输出模块完成。
@@ -1,6 +1,3 @@
1
- """paper-search-carsi: institutional (publisher-authorized) PDF download backend for PaperSearch.
2
-
3
- Derived from scansci-pdf (MIT); only the institutional access phase is kept.
4
- """
1
+ """paper-search-carsi: institutional PDF and supplementary-file download backend for PaperSearch."""
5
2
 
6
3
  __version__ = "0.1.0"
@@ -19,7 +19,7 @@ decorate it with :func:`register`, and import the package below.
19
19
 
20
20
  from .base import PublisherAdapter, SupplementLink, adapter_class, adapter_for, register
21
21
  from . import ( # noqa: F401 (register the adapters)
22
- acm, acs, aha_journals, ams, annual_reviews, aps, asce, bmj, copernicus, dovepress, elsevier, ieee, iop,
22
+ acm, acs, aha_journals, ams, annual_reviews, aps, asce, bmj, copernicus, dovepress, elsevier, frontiers, ieee, iop,
23
23
  jama, karger, lww, mdpi, nejm, neurology, optica, oup, plos, pnas, rsc, sage, science, springer,
24
24
  taylor_francis, thieme, wiley, world_scientific)
25
25
 
@@ -14,6 +14,8 @@ class SupplementLink:
14
14
 
15
15
  url: str
16
16
  label: str = ""
17
+ # Discovery failures are recorded alongside downloaded files.
18
+ error: str = ""
17
19
 
18
20
 
19
21
  class PublisherAdapter:
@@ -0,0 +1,45 @@
1
+ """Frontiers supplementary files on the article's own download panel."""
2
+ from __future__ import annotations
3
+
4
+ import re
5
+ from typing import Any
6
+ from urllib.parse import unquote, urlparse
7
+
8
+ from ..base import PublisherAdapter, SupplementLink, register
9
+
10
+ _SUPPLEMENT_ROWS_JS = """() => ({
11
+ doi: document.querySelector('meta[name="citation_doi"]')?.content || '',
12
+ rows: [...document.querySelectorAll('#supplementaryMaterial a[href], #supplementary-material a[href]')].map(a => ({
13
+ href: a.href,
14
+ label: (a.closest('li')?.innerText || a.innerText || '').replace(/\\s+/g, ' ').trim().slice(0, 240)
15
+ }))
16
+ })"""
17
+
18
+
19
+ def supplement_links_from_page(data: Any, doi: str) -> list[SupplementLink]:
20
+ """The panel, citation DOI and file's article ID must all agree."""
21
+ article = re.fullmatch(r"10\.3389/[a-z0-9]+\.\d{4}\.(\d+)", doi, re.I)
22
+ if (not article or not isinstance(data, dict)
23
+ or str(data.get("doi", "")).strip().lower() != doi.lower()):
24
+ return []
25
+ article_id = article.group(1)
26
+ links: list[SupplementLink] = []
27
+ seen: set[str] = set()
28
+ for row in data.get("rows", []):
29
+ target = urlparse(str(row.get("href") or ""))
30
+ url = target._replace(fragment="").geturl()
31
+ if (target.scheme == "https"
32
+ and re.fullmatch(r"public-pages-files(?:-\d{4})?\.frontiersin\.org", target.hostname or "")
33
+ and unquote(target.path).startswith(f"/articles/{article_id}/file/")
34
+ and url not in seen):
35
+ seen.add(url)
36
+ links.append(SupplementLink(url, str(row.get("label") or "")))
37
+ return links
38
+
39
+
40
+ @register
41
+ class FrontiersAdapter(PublisherAdapter):
42
+ publisher = "frontiers"
43
+
44
+ def supplement_links(self, page: Any, doi: str) -> list[SupplementLink]:
45
+ return supplement_links_from_page(page.evaluate(_SUPPLEMENT_ROWS_JS), doi)
@@ -5,8 +5,37 @@ from the DOI for the journals whose codes are known.
5
5
  """
6
6
  from __future__ import annotations
7
7
 
8
+ import re
9
+ from typing import Any
10
+ from urllib.parse import unquote, urlparse
11
+
8
12
  from ...engine.router import _append_mdpi_candidates
9
- from ..base import PublisherAdapter, register
13
+ from ..base import PublisherAdapter, SupplementLink, register
14
+
15
+ _SUPPLEMENT_ROWS_JS = """() => ({
16
+ doi: document.querySelector('meta[name="citation_doi"]')?.content || '',
17
+ rows: [...document.querySelectorAll('a[href]')].map(a => ({
18
+ href: a.href,
19
+ label: (a.closest('.html-p')?.innerText || a.innerText || '').replace(/\\s+/g, ' ').trim().slice(0, 240)
20
+ }))
21
+ })"""
22
+
23
+
24
+ def supplement_links_from_page(data: Any, doi: str) -> list[SupplementLink]:
25
+ """MDPI's DOI-bound /sN links redirect to the original supporting files."""
26
+ if not isinstance(data, dict) or str(data.get("doi", "")).strip().lower() != doi.lower():
27
+ return []
28
+ pattern = re.compile(r"/article/" + re.escape(doi) + r"/s[1-9][0-9]*", re.I)
29
+ links: list[SupplementLink] = []
30
+ seen: set[str] = set()
31
+ for row in data.get("rows", []):
32
+ target = urlparse(str(row.get("href") or ""))
33
+ url = target._replace(fragment="").geturl()
34
+ if (target.scheme == "https" and target.hostname in {"www.mdpi.com", "mdpi.com"}
35
+ and pattern.fullmatch(unquote(target.path)) and url not in seen):
36
+ seen.add(url)
37
+ links.append(SupplementLink(url, str(row.get("label") or "")))
38
+ return links
10
39
 
11
40
 
12
41
  @register
@@ -18,3 +47,6 @@ class MDPIAdapter(PublisherAdapter):
18
47
  urls: list[str] = []
19
48
  _append_mdpi_candidates(urls, doi, source_url)
20
49
  return urls
50
+
51
+ def supplement_links(self, page: Any, doi: str) -> list[SupplementLink]:
52
+ return supplement_links_from_page(page.evaluate(_SUPPLEMENT_ROWS_JS), doi)