paper-search-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/.env.example +165 -0
  2. package/LICENSE +21 -0
  3. package/README-sc.md +642 -0
  4. package/README.md +642 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +637 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config/ConfigService.d.ts +26 -0
  10. package/dist/config/ConfigService.d.ts.map +1 -0
  11. package/dist/config/ConfigService.js +145 -0
  12. package/dist/config/ConfigService.js.map +1 -0
  13. package/dist/config/constants.d.ts +140 -0
  14. package/dist/config/constants.d.ts.map +1 -0
  15. package/dist/config/constants.js +93 -0
  16. package/dist/config/constants.js.map +1 -0
  17. package/dist/core/diagnostics.d.ts +43 -0
  18. package/dist/core/diagnostics.d.ts.map +1 -0
  19. package/dist/core/diagnostics.js +544 -0
  20. package/dist/core/diagnostics.js.map +1 -0
  21. package/dist/core/handleToolCall.d.ts +8 -0
  22. package/dist/core/handleToolCall.d.ts.map +1 -0
  23. package/dist/core/handleToolCall.js +440 -0
  24. package/dist/core/handleToolCall.js.map +1 -0
  25. package/dist/core/schemas.d.ts +454 -0
  26. package/dist/core/schemas.d.ts.map +1 -0
  27. package/dist/core/schemas.js +322 -0
  28. package/dist/core/schemas.js.map +1 -0
  29. package/dist/core/searchers.d.ts +45 -0
  30. package/dist/core/searchers.d.ts.map +1 -0
  31. package/dist/core/searchers.js +73 -0
  32. package/dist/core/searchers.js.map +1 -0
  33. package/dist/core/tools.d.ts +7 -0
  34. package/dist/core/tools.d.ts.map +1 -0
  35. package/dist/core/tools.js +640 -0
  36. package/dist/core/tools.js.map +1 -0
  37. package/dist/models/Paper.d.ts +64 -0
  38. package/dist/models/Paper.d.ts.map +1 -0
  39. package/dist/models/Paper.js +70 -0
  40. package/dist/models/Paper.js.map +1 -0
  41. package/dist/platforms/ArxivSearcher.d.ts +64 -0
  42. package/dist/platforms/ArxivSearcher.d.ts.map +1 -0
  43. package/dist/platforms/ArxivSearcher.js +531 -0
  44. package/dist/platforms/ArxivSearcher.js.map +1 -0
  45. package/dist/platforms/BioRxivSearcher.d.ts +47 -0
  46. package/dist/platforms/BioRxivSearcher.d.ts.map +1 -0
  47. package/dist/platforms/BioRxivSearcher.js +196 -0
  48. package/dist/platforms/BioRxivSearcher.js.map +1 -0
  49. package/dist/platforms/CORESearcher.d.ts +16 -0
  50. package/dist/platforms/CORESearcher.d.ts.map +1 -0
  51. package/dist/platforms/CORESearcher.js +148 -0
  52. package/dist/platforms/CORESearcher.js.map +1 -0
  53. package/dist/platforms/CrossrefSearcher.d.ts +34 -0
  54. package/dist/platforms/CrossrefSearcher.d.ts.map +1 -0
  55. package/dist/platforms/CrossrefSearcher.js +339 -0
  56. package/dist/platforms/CrossrefSearcher.js.map +1 -0
  57. package/dist/platforms/EuropePMCSearcher.d.ts +20 -0
  58. package/dist/platforms/EuropePMCSearcher.d.ts.map +1 -0
  59. package/dist/platforms/EuropePMCSearcher.js +173 -0
  60. package/dist/platforms/EuropePMCSearcher.js.map +1 -0
  61. package/dist/platforms/GoogleScholarSearcher.d.ts +77 -0
  62. package/dist/platforms/GoogleScholarSearcher.d.ts.map +1 -0
  63. package/dist/platforms/GoogleScholarSearcher.js +262 -0
  64. package/dist/platforms/GoogleScholarSearcher.js.map +1 -0
  65. package/dist/platforms/IACRSearcher.d.ts +51 -0
  66. package/dist/platforms/IACRSearcher.d.ts.map +1 -0
  67. package/dist/platforms/IACRSearcher.js +339 -0
  68. package/dist/platforms/IACRSearcher.js.map +1 -0
  69. package/dist/platforms/OpenAIRESearcher.d.ts +22 -0
  70. package/dist/platforms/OpenAIRESearcher.d.ts.map +1 -0
  71. package/dist/platforms/OpenAIRESearcher.js +223 -0
  72. package/dist/platforms/OpenAIRESearcher.js.map +1 -0
  73. package/dist/platforms/OpenAlexSearcher.d.ts +14 -0
  74. package/dist/platforms/OpenAlexSearcher.d.ts.map +1 -0
  75. package/dist/platforms/OpenAlexSearcher.js +114 -0
  76. package/dist/platforms/OpenAlexSearcher.js.map +1 -0
  77. package/dist/platforms/PMCSearcher.d.ts +20 -0
  78. package/dist/platforms/PMCSearcher.d.ts.map +1 -0
  79. package/dist/platforms/PMCSearcher.js +177 -0
  80. package/dist/platforms/PMCSearcher.js.map +1 -0
  81. package/dist/platforms/PaperSource.d.ts +143 -0
  82. package/dist/platforms/PaperSource.d.ts.map +1 -0
  83. package/dist/platforms/PaperSource.js +125 -0
  84. package/dist/platforms/PaperSource.js.map +1 -0
  85. package/dist/platforms/PubMedSearcher.d.ts +104 -0
  86. package/dist/platforms/PubMedSearcher.d.ts.map +1 -0
  87. package/dist/platforms/PubMedSearcher.js +422 -0
  88. package/dist/platforms/PubMedSearcher.js.map +1 -0
  89. package/dist/platforms/SciHubSearcher.d.ts +66 -0
  90. package/dist/platforms/SciHubSearcher.d.ts.map +1 -0
  91. package/dist/platforms/SciHubSearcher.js +398 -0
  92. package/dist/platforms/SciHubSearcher.js.map +1 -0
  93. package/dist/platforms/ScienceDirectSearcher.d.ts +42 -0
  94. package/dist/platforms/ScienceDirectSearcher.d.ts.map +1 -0
  95. package/dist/platforms/ScienceDirectSearcher.js +326 -0
  96. package/dist/platforms/ScienceDirectSearcher.js.map +1 -0
  97. package/dist/platforms/ScopusSearcher.d.ts +43 -0
  98. package/dist/platforms/ScopusSearcher.d.ts.map +1 -0
  99. package/dist/platforms/ScopusSearcher.js +364 -0
  100. package/dist/platforms/ScopusSearcher.js.map +1 -0
  101. package/dist/platforms/SemanticScholarSearcher.d.ts +96 -0
  102. package/dist/platforms/SemanticScholarSearcher.d.ts.map +1 -0
  103. package/dist/platforms/SemanticScholarSearcher.js +419 -0
  104. package/dist/platforms/SemanticScholarSearcher.js.map +1 -0
  105. package/dist/platforms/SpringerSearcher.d.ts +54 -0
  106. package/dist/platforms/SpringerSearcher.d.ts.map +1 -0
  107. package/dist/platforms/SpringerSearcher.js +407 -0
  108. package/dist/platforms/SpringerSearcher.js.map +1 -0
  109. package/dist/platforms/UnpaywallSearcher.d.ts +18 -0
  110. package/dist/platforms/UnpaywallSearcher.d.ts.map +1 -0
  111. package/dist/platforms/UnpaywallSearcher.js +115 -0
  112. package/dist/platforms/UnpaywallSearcher.js.map +1 -0
  113. package/dist/platforms/WebOfScienceSearcher.d.ts +111 -0
  114. package/dist/platforms/WebOfScienceSearcher.d.ts.map +1 -0
  115. package/dist/platforms/WebOfScienceSearcher.js +500 -0
  116. package/dist/platforms/WebOfScienceSearcher.js.map +1 -0
  117. package/dist/platforms/WileySearcher.d.ts +44 -0
  118. package/dist/platforms/WileySearcher.d.ts.map +1 -0
  119. package/dist/platforms/WileySearcher.js +148 -0
  120. package/dist/platforms/WileySearcher.js.map +1 -0
  121. package/dist/services/CitationService.d.ts +66 -0
  122. package/dist/services/CitationService.d.ts.map +1 -0
  123. package/dist/services/CitationService.js +237 -0
  124. package/dist/services/CitationService.js.map +1 -0
  125. package/dist/services/MultiSourceSearchService.d.ts +19 -0
  126. package/dist/services/MultiSourceSearchService.d.ts.map +1 -0
  127. package/dist/services/MultiSourceSearchService.js +96 -0
  128. package/dist/services/MultiSourceSearchService.js.map +1 -0
  129. package/dist/services/OpenAccessFallbackService.d.ts +20 -0
  130. package/dist/services/OpenAccessFallbackService.d.ts.map +1 -0
  131. package/dist/services/OpenAccessFallbackService.js +124 -0
  132. package/dist/services/OpenAccessFallbackService.js.map +1 -0
  133. package/dist/utils/ErrorHandler.d.ts +99 -0
  134. package/dist/utils/ErrorHandler.d.ts.map +1 -0
  135. package/dist/utils/ErrorHandler.js +266 -0
  136. package/dist/utils/ErrorHandler.js.map +1 -0
  137. package/dist/utils/Logger.d.ts +6 -0
  138. package/dist/utils/Logger.d.ts.map +1 -0
  139. package/dist/utils/Logger.js +26 -0
  140. package/dist/utils/Logger.js.map +1 -0
  141. package/dist/utils/PDFExtractor.d.ts +34 -0
  142. package/dist/utils/PDFExtractor.d.ts.map +1 -0
  143. package/dist/utils/PDFExtractor.js +130 -0
  144. package/dist/utils/PDFExtractor.js.map +1 -0
  145. package/dist/utils/PdfDownload.d.ts +7 -0
  146. package/dist/utils/PdfDownload.d.ts.map +1 -0
  147. package/dist/utils/PdfDownload.js +52 -0
  148. package/dist/utils/PdfDownload.js.map +1 -0
  149. package/dist/utils/QuotaManager.d.ts +32 -0
  150. package/dist/utils/QuotaManager.d.ts.map +1 -0
  151. package/dist/utils/QuotaManager.js +95 -0
  152. package/dist/utils/QuotaManager.js.map +1 -0
  153. package/dist/utils/RateLimiter.d.ts +50 -0
  154. package/dist/utils/RateLimiter.d.ts.map +1 -0
  155. package/dist/utils/RateLimiter.js +121 -0
  156. package/dist/utils/RateLimiter.js.map +1 -0
  157. package/dist/utils/RequestCache.d.ts +26 -0
  158. package/dist/utils/RequestCache.d.ts.map +1 -0
  159. package/dist/utils/RequestCache.js +66 -0
  160. package/dist/utils/RequestCache.js.map +1 -0
  161. package/dist/utils/SecurityUtils.d.ts +80 -0
  162. package/dist/utils/SecurityUtils.d.ts.map +1 -0
  163. package/dist/utils/SecurityUtils.js +357 -0
  164. package/dist/utils/SecurityUtils.js.map +1 -0
  165. package/package.json +111 -0
  166. package/skills/paper-search/SKILL.md +192 -0
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Springer Nature Searcher
3
+ *
4
+ * Documentation: https://dev.springernature.com/
5
+ * API Endpoints:
6
+ * - Metadata API v2: https://api.springernature.com/meta/v2/json
7
+ * - OpenAccess API: https://api.springernature.com/openaccess/json (if available with your key)
8
+ *
9
+ * Required API Key: Yes (api_key parameter)
10
+ * Get API key from: https://dev.springernature.com/signup
11
+ *
12
+ * Note: Meta API v2 is the primary API. OpenAccess API may require special access.
13
+ */
14
+ import axios from 'axios';
15
+ import { sanitizeDoi, escapeQueryValue, withTimeout } from '../utils/SecurityUtils.js';
16
+ import { PaperSource } from './PaperSource.js';
17
+ import { PaperFactory } from '../models/Paper.js';
18
+ import { RateLimiter } from '../utils/RateLimiter.js';
19
+ import { ErrorHandler } from '../utils/ErrorHandler.js';
20
+ import { QuotaManager } from '../utils/QuotaManager.js';
21
+ import { logDebug, logWarn } from '../utils/Logger.js';
22
+ import { TIMEOUTS, USER_AGENT } from '../config/constants.js';
23
+ export class SpringerSearcher extends PaperSource {
24
+ metadataClient;
25
+ openAccessClient;
26
+ rateLimiter;
27
+ quotaManager;
28
+ hasOpenAccessAPI;
29
+ openAccessApiKey;
30
+ testingPromise = null;
31
+ constructor(apiKey, openAccessApiKey) {
32
+ super('springer', 'https://api.springernature.com', apiKey);
33
+ // Check for separate OpenAccess API key from environment
34
+ this.openAccessApiKey = openAccessApiKey || process.env.SPRINGER_OPENACCESS_API_KEY || apiKey;
35
+ // Use v2 API endpoint for metadata
36
+ this.metadataClient = axios.create({
37
+ baseURL: 'https://api.springernature.com/meta/v2',
38
+ timeout: TIMEOUTS.DEFAULT,
39
+ headers: {
40
+ 'Accept': 'application/json',
41
+ 'User-Agent': USER_AGENT
42
+ }
43
+ });
44
+ // OpenAccess API client (may not be available for all API keys)
45
+ this.openAccessClient = axios.create({
46
+ baseURL: 'https://api.springernature.com/openaccess',
47
+ timeout: TIMEOUTS.DEFAULT,
48
+ headers: {
49
+ 'Accept': 'application/json',
50
+ 'User-Agent': USER_AGENT
51
+ }
52
+ });
53
+ // Springer rate limits:
54
+ // - 5000 requests per day for both APIs combined
55
+ // - Approximately 200 per hour or 3-4 per minute to be safe
56
+ // Note: The same API key works for both Metadata and OpenAccess APIs
57
+ this.rateLimiter = new RateLimiter({
58
+ requestsPerSecond: 0.05, // Conservative: 3 per minute
59
+ burstCapacity: 5
60
+ });
61
+ this.quotaManager = QuotaManager.getInstance();
62
+ this.quotaManager.registerPlatform('springer', {
63
+ dailyLimit: 5000,
64
+ envPrefix: 'SPRINGER'
65
+ });
66
+ }
67
+ async search(query, options = {}) {
68
+ const customOptions = options;
69
+ if (!this.apiKey) {
70
+ throw new Error('Springer API key is required');
71
+ }
72
+ const maxResults = Math.min(options.maxResults || 10, 100);
73
+ const papers = [];
74
+ try {
75
+ // Decide which API to use
76
+ let useOpenAccess = customOptions.openAccess === true;
77
+ // If openAccess is requested and we haven't tested the API yet, test it
78
+ if (useOpenAccess && this.hasOpenAccessAPI === undefined) {
79
+ await this.testOpenAccessAPI();
80
+ }
81
+ // Fall back to Meta API if OpenAccess API is not available
82
+ if (useOpenAccess && !this.hasOpenAccessAPI) {
83
+ logDebug('OpenAccess API not available, using Meta API with filtering');
84
+ useOpenAccess = false;
85
+ }
86
+ // Build query parameters
87
+ const params = {
88
+ q: query,
89
+ api_key: useOpenAccess ? this.openAccessApiKey : this.apiKey,
90
+ s: 1, // start index
91
+ p: maxResults // page size
92
+ };
93
+ // Add filters - Note: Some filters may require premium access
94
+ if (options.author) {
95
+ const sanitizedAuthor = this.sanitizeQueryValue(options.author);
96
+ params.q += ` name:"${sanitizedAuthor}"`;
97
+ }
98
+ if (options.journal) {
99
+ const sanitizedJournal = this.sanitizeQueryValue(options.journal);
100
+ params.q += ` pub:"${sanitizedJournal}"`;
101
+ }
102
+ if (options.year) {
103
+ // Year filter may cause 403 for some API keys
104
+ if (options.year.includes('-')) {
105
+ const [startYear, endYear] = options.year.split('-');
106
+ params.q += ` year:${startYear} TO ${endYear || '*'}`;
107
+ }
108
+ else {
109
+ params.q += ` year:${options.year}`;
110
+ }
111
+ }
112
+ if (customOptions.subject) {
113
+ // Subject filter may cause 403 for some API keys
114
+ const sanitizedSubject = this.sanitizeQueryValue(customOptions.subject);
115
+ params.q += ` subject:"${sanitizedSubject}"`;
116
+ }
117
+ if (customOptions.type) {
118
+ // Type filter generally works
119
+ params.q += ` type:${customOptions.type}`;
120
+ }
121
+ await this.rateLimiter.waitForPermission();
122
+ this.quotaManager.checkQuota('springer');
123
+ // Choose the appropriate API
124
+ let response;
125
+ if (useOpenAccess) {
126
+ response = await ErrorHandler.retryWithBackoff(() => this.openAccessClient.get('/json', { params }), { context: 'Springer OpenAccess search' });
127
+ }
128
+ else {
129
+ response = await ErrorHandler.retryWithBackoff(() => this.metadataClient.get('/json', { params }), { context: 'Springer Meta search' });
130
+ }
131
+ this.quotaManager.incrementUsage('springer');
132
+ // Handle different response structures
133
+ // Meta v2 API: records contains the actual papers, result contains metadata
134
+ // OpenAccess API: might use either records or result for the actual papers
135
+ let results = [];
136
+ // For Meta v2 API, records is always the array of papers
137
+ if (response.data.records && Array.isArray(response.data.records)) {
138
+ results = response.data.records;
139
+ }
140
+ // For older API versions or different response format
141
+ else if (response.data.result && Array.isArray(response.data.result) &&
142
+ response.data.result.length > 0 &&
143
+ response.data.result[0].title) {
144
+ // If result contains actual papers (has title field), use it
145
+ results = response.data.result;
146
+ }
147
+ if (results && results.length > 0) {
148
+ for (const result of results) {
149
+ const paper = this.parseResult(result);
150
+ if (paper) {
151
+ // If openAccess filter was requested but using Meta API, filter results
152
+ if (customOptions.openAccess && !useOpenAccess && result.openaccess !== 'true') {
153
+ continue;
154
+ }
155
+ papers.push(paper);
156
+ }
157
+ }
158
+ }
159
+ return papers;
160
+ }
161
+ catch (error) {
162
+ if (error.response?.status === 401) {
163
+ this.handleHttpError(error, 'search');
164
+ }
165
+ if (error.response?.status === 403) {
166
+ // Some filters require premium access
167
+ logWarn('Springer API returned 403 - some filters may require premium access');
168
+ // Try a simpler query without advanced filters
169
+ if (options.year || customOptions.subject) {
170
+ logDebug('Retrying without year/subject filters...');
171
+ const simpleOptions = { ...options };
172
+ delete simpleOptions.year;
173
+ delete simpleOptions.subject;
174
+ return this.search(query, simpleOptions);
175
+ }
176
+ this.handleHttpError(error, 'search');
177
+ }
178
+ if (error.response?.status === 429) {
179
+ this.handleHttpError(error, 'search');
180
+ }
181
+ this.handleHttpError(error, 'search');
182
+ }
183
+ }
184
+ parseResult(result) {
185
+ try {
186
+ // Extract authors
187
+ const authors = result.creators?.map(c => c.creator).join(', ') || '';
188
+ // Extract URL
189
+ let paperUrl;
190
+ let pdfUrl;
191
+ if (result.url && result.url.length > 0) {
192
+ for (const urlObj of result.url) {
193
+ if (urlObj.format === 'pdf') {
194
+ pdfUrl = urlObj.value;
195
+ }
196
+ else if (!paperUrl) {
197
+ paperUrl = urlObj.value;
198
+ }
199
+ }
200
+ }
201
+ // If no URL found, construct from DOI
202
+ if (!paperUrl && result.doi) {
203
+ paperUrl = `https://doi.org/${result.doi}`;
204
+ }
205
+ // Extract page range
206
+ let pages;
207
+ if (result.startingPage && result.endingPage) {
208
+ pages = `${result.startingPage}-${result.endingPage}`;
209
+ }
210
+ else if (result.startingPage) {
211
+ pages = result.startingPage;
212
+ }
213
+ return PaperFactory.create({
214
+ paperId: result.doi || result.identifier || '',
215
+ title: result.title || '',
216
+ authors: authors ? authors.split(', ') : [],
217
+ abstract: result.abstract || '',
218
+ doi: result.doi,
219
+ publishedDate: result.publicationDate ? new Date(result.publicationDate) : null,
220
+ pdfUrl: pdfUrl,
221
+ url: paperUrl,
222
+ source: 'springer',
223
+ journal: result.publicationName,
224
+ volume: result.volume,
225
+ issue: result.number,
226
+ pages: pages,
227
+ extra: {
228
+ isbn: result.isbn,
229
+ issn: result.issn,
230
+ contentType: result.contentType,
231
+ genre: result.genre,
232
+ language: result.language,
233
+ openAccess: result.openaccess === 'true',
234
+ copyright: result.copyright
235
+ }
236
+ });
237
+ }
238
+ catch (error) {
239
+ logDebug('Error parsing Springer result:', error);
240
+ return null;
241
+ }
242
+ }
243
+ async downloadPdf(doi, options = {}) {
244
+ // Search for the paper and check if it has a PDF URL
245
+ const papers = await this.search(doi, { maxResults: 1 });
246
+ if (papers.length === 0) {
247
+ throw new Error('Paper not found');
248
+ }
249
+ if (!papers[0].pdfUrl) {
250
+ // Try searching with openAccess filter to get PDF links
251
+ const openAccessPapers = await this.search(doi, { maxResults: 1, openAccess: true });
252
+ if (openAccessPapers.length === 0 || !openAccessPapers[0].pdfUrl) {
253
+ throw new Error('PDF not available (may require institutional access or not be open access)');
254
+ }
255
+ papers[0] = openAccessPapers[0];
256
+ }
257
+ const paper = papers[0];
258
+ if (!paper.pdfUrl) {
259
+ throw new Error('PDF URL not available for this paper');
260
+ }
261
+ // Download PDF
262
+ const fs = await import('fs');
263
+ const path = await import('path');
264
+ const savePath = options.savePath || './downloads';
265
+ if (!fs.existsSync(savePath)) {
266
+ fs.mkdirSync(savePath, { recursive: true });
267
+ }
268
+ const fileName = `${doi.replace(/[\/\\:*?"<>|]/g, '_')}.pdf`;
269
+ const filePath = path.join(savePath, fileName);
270
+ try {
271
+ const response = await ErrorHandler.retryWithBackoff(() => axios.get(paper.pdfUrl, { responseType: 'stream' }), { context: 'Springer download' });
272
+ const writer = fs.createWriteStream(filePath);
273
+ response.data.pipe(writer);
274
+ return new Promise((resolve, reject) => {
275
+ writer.on('finish', () => resolve(filePath));
276
+ writer.on('error', reject);
277
+ });
278
+ }
279
+ catch (error) {
280
+ throw new Error(`Failed to download PDF: ${error.message}`);
281
+ }
282
+ }
283
+ getCapabilities() {
284
+ return {
285
+ search: true,
286
+ download: true,
287
+ fullText: false,
288
+ citations: true,
289
+ requiresApiKey: true,
290
+ supportedOptions: ['maxResults', 'year', 'author', 'journal', 'openAccess', 'subject', 'type']
291
+ };
292
+ }
293
+ /**
294
+ * 获取引用此论文的文献列表(通过Crossref/OpenCitations)
295
+ */
296
+ async getCitations(doi) {
297
+ try {
298
+ // Validate DOI format
299
+ if (!doi || typeof doi !== 'string') {
300
+ throw new Error('Invalid DOI: must be a non-empty string');
301
+ }
302
+ // Sanitize and validate DOI
303
+ const doiValidation = sanitizeDoi(doi);
304
+ if (!doiValidation.valid) {
305
+ throw new Error(`Invalid DOI: ${doiValidation.error}`);
306
+ }
307
+ const { CrossrefSearcher } = await import('./CrossrefSearcher.js');
308
+ const crossref = new CrossrefSearcher();
309
+ // Add timeout wrapper for Crossref API calls
310
+ return await withTimeout(crossref.getCitations(doiValidation.sanitized), TIMEOUTS.HEALTH_CHECK, 'Crossref citation request timed out');
311
+ }
312
+ catch (error) {
313
+ // Don't log the DOI in case it's sensitive
314
+ logDebug('Error getting Springer citations:', error instanceof Error ? error.message : 'Unknown error');
315
+ return [];
316
+ }
317
+ }
318
+ /**
319
+ * 获取论文的参考文献列表(通过Crossref)
320
+ */
321
+ async getReferences(doi) {
322
+ try {
323
+ // Validate DOI format
324
+ if (!doi || typeof doi !== 'string') {
325
+ throw new Error('Invalid DOI: must be a non-empty string');
326
+ }
327
+ // Sanitize and validate DOI
328
+ const doiValidation = sanitizeDoi(doi);
329
+ if (!doiValidation.valid) {
330
+ throw new Error(`Invalid DOI: ${doiValidation.error}`);
331
+ }
332
+ const { CrossrefSearcher } = await import('./CrossrefSearcher.js');
333
+ const crossref = new CrossrefSearcher();
334
+ // Add timeout wrapper for Crossref API calls
335
+ return await withTimeout(crossref.getReferences(doiValidation.sanitized), TIMEOUTS.HEALTH_CHECK, 'Crossref references request timed out');
336
+ }
337
+ catch (error) {
338
+ // Don't log the DOI in case it's sensitive
339
+ logDebug('Error getting Springer references:', error instanceof Error ? error.message : 'Unknown error');
340
+ return [];
341
+ }
342
+ }
343
+ /**
344
+ * 清理和转义查询参数中的特殊字符
345
+ */
346
+ sanitizeQueryValue(value, context = 'general') {
347
+ return escapeQueryValue(value, 'springer');
348
+ }
349
+ /**
350
+ * Test if OpenAccess API is available for this API key
351
+ * Uses promise caching to prevent race conditions with concurrent requests
352
+ */
353
+ async testOpenAccessAPI() {
354
+ // Already tested
355
+ if (this.hasOpenAccessAPI !== undefined) {
356
+ return;
357
+ }
358
+ // Test already in progress - wait for it
359
+ if (this.testingPromise) {
360
+ return this.testingPromise;
361
+ }
362
+ // Start new test and cache the promise
363
+ this.testingPromise = this.performOpenAccessTest();
364
+ try {
365
+ await this.testingPromise;
366
+ }
367
+ finally {
368
+ this.testingPromise = null;
369
+ }
370
+ }
371
+ /**
372
+ * Perform the actual OpenAccess API test
373
+ */
374
+ async performOpenAccessTest() {
375
+ try {
376
+ const response = await this.openAccessClient.get('/json', {
377
+ params: {
378
+ q: 'test',
379
+ api_key: this.openAccessApiKey,
380
+ s: 1,
381
+ p: 1
382
+ }
383
+ });
384
+ this.hasOpenAccessAPI = response.status === 200;
385
+ logDebug('OpenAccess API is available');
386
+ }
387
+ catch (error) {
388
+ if (error.response?.status === 401) {
389
+ this.hasOpenAccessAPI = false;
390
+ logDebug('OpenAccess API is not available (401 Unauthorized - check API key permissions)');
391
+ }
392
+ else {
393
+ // Network error or other issue, assume not available
394
+ this.hasOpenAccessAPI = false;
395
+ logDebug('OpenAccess API test failed:', error.message);
396
+ }
397
+ }
398
+ }
399
+ async readPaper(paperId, options = {}) {
400
+ const papers = await this.search(paperId, { maxResults: 1 });
401
+ if (papers.length === 0) {
402
+ throw new Error('Paper not found');
403
+ }
404
+ return papers[0].abstract || 'Abstract not available';
405
+ }
406
+ }
407
+ //# sourceMappingURL=SpringerSearcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpringerSearcher.js","sourceRoot":"","sources":["../../src/platforms/SpringerSearcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { Paper } from '../models/Paper.js';
2
+ import { PaperSource, SearchOptions, DownloadOptions, PlatformCapabilities } from './PaperSource.js';
3
+ export declare class UnpaywallSearcher extends PaperSource {
4
+ private readonly client;
5
+ private readonly email;
6
+ constructor(email?: string);
7
+ getCapabilities(): PlatformCapabilities;
8
+ hasApiKey(): boolean;
9
+ search(query: string, _options?: SearchOptions): Promise<Paper[]>;
10
+ getPaperByDoi(doi: string): Promise<Paper | null>;
11
+ resolveBestPdfUrl(doi: string): Promise<string>;
12
+ downloadPdf(_paperId: string, _options?: DownloadOptions): Promise<string>;
13
+ readPaper(_paperId: string, _options?: DownloadOptions): Promise<string>;
14
+ private parseRecord;
15
+ private firstLocationUrl;
16
+ private extractDoi;
17
+ }
18
+ //# sourceMappingURL=UnpaywallSearcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnpaywallSearcher.d.ts","sourceRoot":"","sources":["../../src/platforms/UnpaywallSearcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAgB,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AA+BrG,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,KAAK,CAAC,EAAE,MAAM;IAgB1B,eAAe,IAAI,oBAAoB;IAWvC,SAAS,IAAI,OAAO;IAId,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAkB,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAQrE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAqBjD,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAI9E,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlF,OAAO,CAAC,WAAW;IA6BnB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,UAAU;CAKnB"}
@@ -0,0 +1,115 @@
1
+ import axios from 'axios';
2
+ import { PaperFactory } from '../models/Paper.js';
3
+ import { PaperSource } from './PaperSource.js';
4
+ import { TIMEOUTS, USER_AGENT } from '../config/constants.js';
5
+ import { sanitizeDoi } from '../utils/SecurityUtils.js';
6
+ export class UnpaywallSearcher extends PaperSource {
7
+ client;
8
+ email;
9
+ constructor(email) {
10
+ const configuredEmail = email || process.env.PAPER_SEARCH_UNPAYWALL_EMAIL || process.env.UNPAYWALL_EMAIL || '';
11
+ super('unpaywall', 'https://api.unpaywall.org/v2', configuredEmail);
12
+ this.email = configuredEmail.trim();
13
+ this.client = axios.create({
14
+ baseURL: this.baseUrl,
15
+ timeout: TIMEOUTS.DEFAULT,
16
+ headers: {
17
+ Accept: 'application/json',
18
+ 'User-Agent': `${USER_AGENT} (https://github.com/dr-dumpling/paper-search-cli)`
19
+ },
20
+ validateStatus: status => status < 500
21
+ });
22
+ }
23
+ getCapabilities() {
24
+ return {
25
+ search: true,
26
+ download: false,
27
+ fullText: false,
28
+ citations: false,
29
+ requiresApiKey: true,
30
+ supportedOptions: ['maxResults']
31
+ };
32
+ }
33
+ hasApiKey() {
34
+ return Boolean(this.email);
35
+ }
36
+ async search(query, _options = {}) {
37
+ const doi = this.extractDoi(query);
38
+ if (!doi || !this.email)
39
+ return [];
40
+ const paper = await this.getPaperByDoi(doi);
41
+ return paper ? [paper] : [];
42
+ }
43
+ async getPaperByDoi(doi) {
44
+ if (!this.email)
45
+ return null;
46
+ const cleanDoi = this.extractDoi(doi);
47
+ if (!cleanDoi)
48
+ return null;
49
+ try {
50
+ const response = await this.client.get(`/${encodeURIComponent(cleanDoi)}`, {
51
+ params: { email: this.email }
52
+ });
53
+ if (response.status === 404 || response.status === 422)
54
+ return null;
55
+ if (response.status >= 400) {
56
+ throw new Error(`Unpaywall request failed with HTTP ${response.status}`);
57
+ }
58
+ return this.parseRecord(response.data, cleanDoi);
59
+ }
60
+ catch (error) {
61
+ this.handleHttpError(error, 'getPaperByDoi');
62
+ }
63
+ }
64
+ async resolveBestPdfUrl(doi) {
65
+ const paper = await this.getPaperByDoi(doi);
66
+ return paper?.pdfUrl || '';
67
+ }
68
+ async downloadPdf(_paperId, _options = {}) {
69
+ throw new Error('Unpaywall does not host PDFs. Use returned pdf_url with download_with_fallback.');
70
+ }
71
+ async readPaper(_paperId, _options = {}) {
72
+ throw new Error('Unpaywall provides OA metadata only; it does not provide full text.');
73
+ }
74
+ parseRecord(record, doi) {
75
+ const best = record.best_oa_location || {};
76
+ const pdfUrl = best.url_for_pdf || best.url || this.firstLocationUrl(record.oa_locations || []);
77
+ const authors = (record.z_authors || [])
78
+ .map(author => `${author.given || ''} ${author.family || ''}`.trim())
79
+ .filter(Boolean);
80
+ return PaperFactory.create({
81
+ paperId: `unpaywall:${doi}`,
82
+ title: record.title || doi,
83
+ authors,
84
+ abstract: '',
85
+ doi: record.doi || doi,
86
+ publishedDate: record.published_date ? this.parseDate(record.published_date) : null,
87
+ pdfUrl: pdfUrl || '',
88
+ url: best.url || record.doi_url || `https://doi.org/${doi}`,
89
+ source: 'unpaywall',
90
+ journal: record.journal_name || '',
91
+ extra: {
92
+ isOpenAccess: Boolean(record.is_oa),
93
+ oaStatus: record.oa_status || '',
94
+ publisher: record.publisher || '',
95
+ hostType: best.host_type || '',
96
+ license: best.license || '',
97
+ version: best.version || ''
98
+ }
99
+ });
100
+ }
101
+ firstLocationUrl(locations) {
102
+ for (const location of locations) {
103
+ const candidate = location.url_for_pdf || location.url || '';
104
+ if (candidate)
105
+ return candidate;
106
+ }
107
+ return '';
108
+ }
109
+ extractDoi(value) {
110
+ const candidate = value.match(/10\.\d{4,9}\/[-._;()/:A-Z0-9]+/i)?.[0] || value;
111
+ const result = sanitizeDoi(candidate);
112
+ return result.valid ? result.sanitized : '';
113
+ }
114
+ }
115
+ //# sourceMappingURL=UnpaywallSearcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnpaywallSearcher.js","sourceRoot":"","sources":["../../src/platforms/UnpaywallSearcher.ts"],"names":[],"mappings":"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"}