papersearch 0.0.0-stage → 0.4.2

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 (686) hide show
  1. package/.env.example +208 -0
  2. package/LICENSE +21 -0
  3. package/README.md +213 -2
  4. package/README.zh.md +214 -0
  5. package/carsi/LICENSE +21 -0
  6. package/carsi/NOTICE +5 -0
  7. package/carsi/README.md +75 -0
  8. package/carsi/paper_search_carsi/__init__.py +6 -0
  9. package/carsi/paper_search_carsi/_core/__init__.py +26 -0
  10. package/carsi/paper_search_carsi/_core/racing.pyx +280 -0
  11. package/carsi/paper_search_carsi/adapters/__init__.py +26 -0
  12. package/carsi/paper_search_carsi/adapters/acm/__init__.py +13 -0
  13. package/carsi/paper_search_carsi/adapters/acs/__init__.py +47 -0
  14. package/carsi/paper_search_carsi/adapters/aha_journals/__init__.py +13 -0
  15. package/carsi/paper_search_carsi/adapters/ams/__init__.py +19 -0
  16. package/carsi/paper_search_carsi/adapters/annual_reviews/__init__.py +154 -0
  17. package/carsi/paper_search_carsi/adapters/aps/__init__.py +127 -0
  18. package/carsi/paper_search_carsi/adapters/asce/__init__.py +13 -0
  19. package/carsi/paper_search_carsi/adapters/base.py +320 -0
  20. package/carsi/paper_search_carsi/adapters/bmj/__init__.py +32 -0
  21. package/carsi/paper_search_carsi/adapters/copernicus/__init__.py +20 -0
  22. package/carsi/paper_search_carsi/adapters/dovepress/__init__.py +43 -0
  23. package/carsi/paper_search_carsi/adapters/elsevier/__init__.py +302 -0
  24. package/carsi/paper_search_carsi/adapters/elsevier/journal_site.py +237 -0
  25. package/carsi/paper_search_carsi/adapters/elsevier/sciencedirect.py +138 -0
  26. package/carsi/paper_search_carsi/adapters/ieee/__init__.py +184 -0
  27. package/carsi/paper_search_carsi/adapters/iop/__init__.py +41 -0
  28. package/carsi/paper_search_carsi/adapters/jama/__init__.py +54 -0
  29. package/carsi/paper_search_carsi/adapters/karger/__init__.py +75 -0
  30. package/carsi/paper_search_carsi/adapters/lww/__init__.py +103 -0
  31. package/carsi/paper_search_carsi/adapters/lww/journals_lww.py +36 -0
  32. package/carsi/paper_search_carsi/adapters/lww/ovid.py +72 -0
  33. package/carsi/paper_search_carsi/adapters/mdpi/__init__.py +20 -0
  34. package/carsi/paper_search_carsi/adapters/nejm/__init__.py +18 -0
  35. package/carsi/paper_search_carsi/adapters/neurology/__init__.py +13 -0
  36. package/carsi/paper_search_carsi/adapters/optica/__init__.py +33 -0
  37. package/carsi/paper_search_carsi/adapters/oup/__init__.py +366 -0
  38. package/carsi/paper_search_carsi/adapters/platforms/__init__.py +5 -0
  39. package/carsi/paper_search_carsi/adapters/platforms/atypon/__init__.py +149 -0
  40. package/carsi/paper_search_carsi/adapters/platforms/silverchair/__init__.py +83 -0
  41. package/carsi/paper_search_carsi/adapters/plos/__init__.py +20 -0
  42. package/carsi/paper_search_carsi/adapters/pnas/__init__.py +13 -0
  43. package/carsi/paper_search_carsi/adapters/rsc/__init__.py +74 -0
  44. package/carsi/paper_search_carsi/adapters/sage/__init__.py +154 -0
  45. package/carsi/paper_search_carsi/adapters/science/__init__.py +13 -0
  46. package/carsi/paper_search_carsi/adapters/springer/__init__.py +62 -0
  47. package/carsi/paper_search_carsi/adapters/taylor_francis/__init__.py +46 -0
  48. package/carsi/paper_search_carsi/adapters/thieme/__init__.py +164 -0
  49. package/carsi/paper_search_carsi/adapters/wiley/__init__.py +145 -0
  50. package/carsi/paper_search_carsi/adapters/world_scientific/__init__.py +27 -0
  51. package/carsi/paper_search_carsi/assets/icon-32.png +0 -0
  52. package/carsi/paper_search_carsi/assets/icon-512.png +0 -0
  53. package/carsi/paper_search_carsi/assets/icon-64.png +0 -0
  54. package/carsi/paper_search_carsi/assets/icon.svg +43 -0
  55. package/carsi/paper_search_carsi/blocked_journals.py +63 -0
  56. package/carsi/paper_search_carsi/browser/__init__.py +1 -0
  57. package/carsi/paper_search_carsi/browser/backend.py +823 -0
  58. package/carsi/paper_search_carsi/browser/cookies.py +386 -0
  59. package/carsi/paper_search_carsi/browser/ego_relay.py +152 -0
  60. package/carsi/paper_search_carsi/browser/engine.py +927 -0
  61. package/carsi/paper_search_carsi/browser/login.py +336 -0
  62. package/carsi/paper_search_carsi/browser/preview.py +379 -0
  63. package/carsi/paper_search_carsi/browser/watchdog.py +193 -0
  64. package/carsi/paper_search_carsi/cache.py +62 -0
  65. package/carsi/paper_search_carsi/carsi_schools.py +109 -0
  66. package/carsi/paper_search_carsi/cli.py +479 -0
  67. package/carsi/paper_search_carsi/cold_start.py +251 -0
  68. package/carsi/paper_search_carsi/config.py +301 -0
  69. package/carsi/paper_search_carsi/data/institutional_identity_policy.json +165 -0
  70. package/carsi/paper_search_carsi/data/publisher_access_catalog.json +969 -0
  71. package/carsi/paper_search_carsi/data/publisher_browser_verification_matrix.json +625 -0
  72. package/carsi/paper_search_carsi/download_diagnostics.py +228 -0
  73. package/carsi/paper_search_carsi/engine/__init__.py +1 -0
  74. package/carsi/paper_search_carsi/engine/access_rules.py +345 -0
  75. package/carsi/paper_search_carsi/engine/batch.py +702 -0
  76. package/carsi/paper_search_carsi/engine/batch_access.py +483 -0
  77. package/carsi/paper_search_carsi/engine/batch_capture.py +1531 -0
  78. package/carsi/paper_search_carsi/engine/batch_common.py +427 -0
  79. package/carsi/paper_search_carsi/engine/batch_diagnostics.py +307 -0
  80. package/carsi/paper_search_carsi/engine/batch_institution.py +981 -0
  81. package/carsi/paper_search_carsi/engine/batch_login.py +1605 -0
  82. package/carsi/paper_search_carsi/engine/batch_pdf_urls.py +274 -0
  83. package/carsi/paper_search_carsi/engine/batch_runs.py +711 -0
  84. package/carsi/paper_search_carsi/engine/batch_user.py +173 -0
  85. package/carsi/paper_search_carsi/engine/batch_waits.py +441 -0
  86. package/carsi/paper_search_carsi/engine/bridge.py +666 -0
  87. package/carsi/paper_search_carsi/engine/pdf_guard.py +310 -0
  88. package/carsi/paper_search_carsi/engine/router.py +514 -0
  89. package/carsi/paper_search_carsi/engine/supplements.py +148 -0
  90. package/carsi/paper_search_carsi/entitlements.py +233 -0
  91. package/carsi/paper_search_carsi/extractors/__init__.py +0 -0
  92. package/carsi/paper_search_carsi/extractors/pdf_extractor.py +149 -0
  93. package/carsi/paper_search_carsi/identifiers.py +121 -0
  94. package/carsi/paper_search_carsi/issues.py +227 -0
  95. package/carsi/paper_search_carsi/log.py +84 -0
  96. package/carsi/paper_search_carsi/mcp_progress.py +82 -0
  97. package/carsi/paper_search_carsi/network.py +359 -0
  98. package/carsi/paper_search_carsi/orchestrator.py +130 -0
  99. package/carsi/paper_search_carsi/pdf_utils.py +279 -0
  100. package/carsi/paper_search_carsi/progress_bar.py +778 -0
  101. package/carsi/paper_search_carsi/progress_reporter.py +530 -0
  102. package/carsi/paper_search_carsi/progress_status.py +212 -0
  103. package/carsi/paper_search_carsi/publishers/__init__.py +10 -0
  104. package/carsi/paper_search_carsi/publishers/builtin/acm.json +65 -0
  105. package/carsi/paper_search_carsi/publishers/builtin/acs.json +73 -0
  106. package/carsi/paper_search_carsi/publishers/builtin/aha-journals.json +47 -0
  107. package/carsi/paper_search_carsi/publishers/builtin/aip.json +66 -0
  108. package/carsi/paper_search_carsi/publishers/builtin/ams.json +66 -0
  109. package/carsi/paper_search_carsi/publishers/builtin/annual-reviews.json +64 -0
  110. package/carsi/paper_search_carsi/publishers/builtin/aps.json +70 -0
  111. package/carsi/paper_search_carsi/publishers/builtin/asce.json +46 -0
  112. package/carsi/paper_search_carsi/publishers/builtin/bmj.json +46 -0
  113. package/carsi/paper_search_carsi/publishers/builtin/copernicus.json +45 -0
  114. package/carsi/paper_search_carsi/publishers/builtin/dovepress.json +35 -0
  115. package/carsi/paper_search_carsi/publishers/builtin/elsevier.json +77 -0
  116. package/carsi/paper_search_carsi/publishers/builtin/frontiers.json +62 -0
  117. package/carsi/paper_search_carsi/publishers/builtin/ieee.json +68 -0
  118. package/carsi/paper_search_carsi/publishers/builtin/iop.json +69 -0
  119. package/carsi/paper_search_carsi/publishers/builtin/jama.json +48 -0
  120. package/carsi/paper_search_carsi/publishers/builtin/karger.json +48 -0
  121. package/carsi/paper_search_carsi/publishers/builtin/lww.json +57 -0
  122. package/carsi/paper_search_carsi/publishers/builtin/mdpi.json +46 -0
  123. package/carsi/paper_search_carsi/publishers/builtin/nejm.json +46 -0
  124. package/carsi/paper_search_carsi/publishers/builtin/neurology.json +45 -0
  125. package/carsi/paper_search_carsi/publishers/builtin/optica.json +77 -0
  126. package/carsi/paper_search_carsi/publishers/builtin/oxfordacademic.json +64 -0
  127. package/carsi/paper_search_carsi/publishers/builtin/plos.json +46 -0
  128. package/carsi/paper_search_carsi/publishers/builtin/pnas.json +67 -0
  129. package/carsi/paper_search_carsi/publishers/builtin/royalsocietypublishing.json +62 -0
  130. package/carsi/paper_search_carsi/publishers/builtin/rsc.json +71 -0
  131. package/carsi/paper_search_carsi/publishers/builtin/sage.json +48 -0
  132. package/carsi/paper_search_carsi/publishers/builtin/science.json +68 -0
  133. package/carsi/paper_search_carsi/publishers/builtin/springer.json +92 -0
  134. package/carsi/paper_search_carsi/publishers/builtin/taylor-francis.json +52 -0
  135. package/carsi/paper_search_carsi/publishers/builtin/thieme.json +50 -0
  136. package/carsi/paper_search_carsi/publishers/builtin/wiley.json +77 -0
  137. package/carsi/paper_search_carsi/publishers/builtin/world-scientific.json +68 -0
  138. package/carsi/paper_search_carsi/publishers/extensions.py +241 -0
  139. package/carsi/paper_search_carsi/publishers/loader.py +93 -0
  140. package/carsi/paper_search_carsi/publishers/model.py +90 -0
  141. package/carsi/paper_search_carsi/publishers/profiles.py +127 -0
  142. package/carsi/paper_search_carsi/publishers/schema.json +129 -0
  143. package/carsi/paper_search_carsi/publishers/site.py +86 -0
  144. package/carsi/paper_search_carsi/rate_limit.py +51 -0
  145. package/carsi/paper_search_carsi/server.py +197 -0
  146. package/carsi/paper_search_carsi/session_broker.py +163 -0
  147. package/carsi/paper_search_carsi/tor.py +9 -0
  148. package/carsi/paper_search_carsi/user_settings.py +55 -0
  149. package/carsi/pyproject.toml +41 -0
  150. package/dist/capabilities/body-snippet-search/handler.d.ts +9 -0
  151. package/dist/capabilities/body-snippet-search/handler.d.ts.map +1 -0
  152. package/dist/capabilities/body-snippet-search/handler.js +17 -0
  153. package/dist/capabilities/body-snippet-search/handler.js.map +1 -0
  154. package/dist/capabilities/body-snippet-search/index.d.ts +7 -0
  155. package/dist/capabilities/body-snippet-search/index.d.ts.map +1 -0
  156. package/dist/capabilities/body-snippet-search/index.js +6 -0
  157. package/dist/capabilities/body-snippet-search/index.js.map +1 -0
  158. package/dist/capabilities/body-snippet-search/schemas.d.ts +36 -0
  159. package/dist/capabilities/body-snippet-search/schemas.d.ts.map +1 -0
  160. package/dist/capabilities/body-snippet-search/schemas.js +16 -0
  161. package/dist/capabilities/body-snippet-search/schemas.js.map +1 -0
  162. package/dist/capabilities/body-snippet-search/service.d.ts +4 -0
  163. package/dist/capabilities/body-snippet-search/service.d.ts.map +1 -0
  164. package/dist/capabilities/body-snippet-search/service.js +4 -0
  165. package/dist/capabilities/body-snippet-search/service.js.map +1 -0
  166. package/dist/capabilities/body-snippet-search/tools.d.ts +187 -0
  167. package/dist/capabilities/body-snippet-search/tools.d.ts.map +1 -0
  168. package/dist/capabilities/body-snippet-search/tools.js +49 -0
  169. package/dist/capabilities/body-snippet-search/tools.js.map +1 -0
  170. package/dist/capabilities/body-snippet-search/types.d.ts +14 -0
  171. package/dist/capabilities/body-snippet-search/types.d.ts.map +1 -0
  172. package/dist/capabilities/body-snippet-search/types.js +2 -0
  173. package/dist/capabilities/body-snippet-search/types.js.map +1 -0
  174. package/dist/capabilities/citation-expansion/CitationService.d.ts +47 -0
  175. package/dist/capabilities/citation-expansion/CitationService.d.ts.map +1 -0
  176. package/dist/capabilities/citation-expansion/CitationService.js +243 -0
  177. package/dist/capabilities/citation-expansion/CitationService.js.map +1 -0
  178. package/dist/capabilities/citation-expansion/handler.d.ts +36 -0
  179. package/dist/capabilities/citation-expansion/handler.d.ts.map +1 -0
  180. package/dist/capabilities/citation-expansion/handler.js +44 -0
  181. package/dist/capabilities/citation-expansion/handler.js.map +1 -0
  182. package/dist/capabilities/citation-expansion/index.d.ts +6 -0
  183. package/dist/capabilities/citation-expansion/index.d.ts.map +1 -0
  184. package/dist/capabilities/citation-expansion/index.js +5 -0
  185. package/dist/capabilities/citation-expansion/index.js.map +1 -0
  186. package/dist/capabilities/citation-expansion/schemas.d.ts +28 -0
  187. package/dist/capabilities/citation-expansion/schemas.d.ts.map +1 -0
  188. package/dist/capabilities/citation-expansion/schemas.js +13 -0
  189. package/dist/capabilities/citation-expansion/schemas.js.map +1 -0
  190. package/dist/capabilities/citation-expansion/tools.d.ts +82 -0
  191. package/dist/capabilities/citation-expansion/tools.d.ts.map +1 -0
  192. package/dist/capabilities/citation-expansion/tools.js +48 -0
  193. package/dist/capabilities/citation-expansion/tools.js.map +1 -0
  194. package/dist/capabilities/citation-expansion/types.d.ts +22 -0
  195. package/dist/capabilities/citation-expansion/types.d.ts.map +1 -0
  196. package/dist/capabilities/citation-expansion/types.js +2 -0
  197. package/dist/capabilities/citation-expansion/types.js.map +1 -0
  198. package/dist/capabilities/journal-metrics/JournalMetricsService.d.ts +3 -0
  199. package/dist/capabilities/journal-metrics/JournalMetricsService.d.ts.map +1 -0
  200. package/dist/capabilities/journal-metrics/JournalMetricsService.js +142 -0
  201. package/dist/capabilities/journal-metrics/JournalMetricsService.js.map +1 -0
  202. package/dist/capabilities/journal-metrics/handler.d.ts +12 -0
  203. package/dist/capabilities/journal-metrics/handler.d.ts.map +1 -0
  204. package/dist/capabilities/journal-metrics/handler.js +38 -0
  205. package/dist/capabilities/journal-metrics/handler.js.map +1 -0
  206. package/dist/capabilities/journal-metrics/index.d.ts +6 -0
  207. package/dist/capabilities/journal-metrics/index.d.ts.map +1 -0
  208. package/dist/capabilities/journal-metrics/index.js +5 -0
  209. package/dist/capabilities/journal-metrics/index.js.map +1 -0
  210. package/dist/capabilities/journal-metrics/schemas.d.ts +28 -0
  211. package/dist/capabilities/journal-metrics/schemas.d.ts.map +1 -0
  212. package/dist/capabilities/journal-metrics/schemas.js +13 -0
  213. package/dist/capabilities/journal-metrics/schemas.js.map +1 -0
  214. package/dist/capabilities/journal-metrics/tools.d.ts +67 -0
  215. package/dist/capabilities/journal-metrics/tools.d.ts.map +1 -0
  216. package/dist/capabilities/journal-metrics/tools.js +27 -0
  217. package/dist/capabilities/journal-metrics/tools.js.map +1 -0
  218. package/dist/capabilities/journal-metrics/types.d.ts +36 -0
  219. package/dist/capabilities/journal-metrics/types.d.ts.map +1 -0
  220. package/dist/capabilities/journal-metrics/types.js +2 -0
  221. package/dist/capabilities/journal-metrics/types.js.map +1 -0
  222. package/dist/capabilities/metadata-search/MultiSourceSearchService.d.ts +9 -0
  223. package/dist/capabilities/metadata-search/MultiSourceSearchService.d.ts.map +1 -0
  224. package/dist/capabilities/metadata-search/MultiSourceSearchService.js +81 -0
  225. package/dist/capabilities/metadata-search/MultiSourceSearchService.js.map +1 -0
  226. package/dist/capabilities/metadata-search/handler.d.ts +23 -0
  227. package/dist/capabilities/metadata-search/handler.d.ts.map +1 -0
  228. package/dist/capabilities/metadata-search/handler.js +153 -0
  229. package/dist/capabilities/metadata-search/handler.js.map +1 -0
  230. package/dist/capabilities/metadata-search/index.d.ts +6 -0
  231. package/dist/capabilities/metadata-search/index.d.ts.map +1 -0
  232. package/dist/capabilities/metadata-search/index.js +5 -0
  233. package/dist/capabilities/metadata-search/index.js.map +1 -0
  234. package/dist/capabilities/metadata-search/schemas.d.ts +83 -0
  235. package/dist/capabilities/metadata-search/schemas.d.ts.map +1 -0
  236. package/dist/capabilities/metadata-search/schemas.js +80 -0
  237. package/dist/capabilities/metadata-search/schemas.js.map +1 -0
  238. package/dist/capabilities/metadata-search/tools.d.ts +178 -0
  239. package/dist/capabilities/metadata-search/tools.d.ts.map +1 -0
  240. package/dist/capabilities/metadata-search/tools.js +75 -0
  241. package/dist/capabilities/metadata-search/tools.js.map +1 -0
  242. package/dist/capabilities/metadata-search/types.d.ts +13 -0
  243. package/dist/capabilities/metadata-search/types.d.ts.map +1 -0
  244. package/dist/capabilities/metadata-search/types.js +2 -0
  245. package/dist/capabilities/metadata-search/types.js.map +1 -0
  246. package/dist/capabilities/pdf-discovery/DownloadTier.d.ts +43 -0
  247. package/dist/capabilities/pdf-discovery/DownloadTier.d.ts.map +1 -0
  248. package/dist/capabilities/pdf-discovery/DownloadTier.js +2 -0
  249. package/dist/capabilities/pdf-discovery/DownloadTier.js.map +1 -0
  250. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.d.ts +16 -0
  251. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.d.ts.map +1 -0
  252. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js +202 -0
  253. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js.map +1 -0
  254. package/dist/capabilities/pdf-discovery/batch.d.ts +74 -0
  255. package/dist/capabilities/pdf-discovery/batch.d.ts.map +1 -0
  256. package/dist/capabilities/pdf-discovery/batch.js +370 -0
  257. package/dist/capabilities/pdf-discovery/batch.js.map +1 -0
  258. package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.d.ts +7 -0
  259. package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.d.ts.map +1 -0
  260. package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.js +46 -0
  261. package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.js.map +1 -0
  262. package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.d.ts +35 -0
  263. package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.d.ts.map +1 -0
  264. package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.js +689 -0
  265. package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.js.map +1 -0
  266. package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.d.ts +23 -0
  267. package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.d.ts.map +1 -0
  268. package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.js +30 -0
  269. package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.js.map +1 -0
  270. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.d.ts +29 -0
  271. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.d.ts.map +1 -0
  272. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.js +176 -0
  273. package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.js.map +1 -0
  274. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.d.ts +8 -0
  275. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.d.ts.map +1 -0
  276. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.js +225 -0
  277. package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.js.map +1 -0
  278. package/dist/capabilities/pdf-discovery/carsi/index.d.ts +8 -0
  279. package/dist/capabilities/pdf-discovery/carsi/index.d.ts.map +1 -0
  280. package/dist/capabilities/pdf-discovery/carsi/index.js +11 -0
  281. package/dist/capabilities/pdf-discovery/carsi/index.js.map +1 -0
  282. package/dist/capabilities/pdf-discovery/carsi/types.d.ts +118 -0
  283. package/dist/capabilities/pdf-discovery/carsi/types.d.ts.map +1 -0
  284. package/dist/capabilities/pdf-discovery/carsi/types.js +2 -0
  285. package/dist/capabilities/pdf-discovery/carsi/types.js.map +1 -0
  286. package/dist/capabilities/pdf-discovery/handler.d.ts +28 -0
  287. package/dist/capabilities/pdf-discovery/handler.d.ts.map +1 -0
  288. package/dist/capabilities/pdf-discovery/handler.js +72 -0
  289. package/dist/capabilities/pdf-discovery/handler.js.map +1 -0
  290. package/dist/capabilities/pdf-discovery/identity.d.ts +8 -0
  291. package/dist/capabilities/pdf-discovery/identity.d.ts.map +1 -0
  292. package/dist/capabilities/pdf-discovery/identity.js +24 -0
  293. package/dist/capabilities/pdf-discovery/identity.js.map +1 -0
  294. package/dist/capabilities/pdf-discovery/index.d.ts +8 -0
  295. package/dist/capabilities/pdf-discovery/index.d.ts.map +1 -0
  296. package/dist/capabilities/pdf-discovery/index.js +6 -0
  297. package/dist/capabilities/pdf-discovery/index.js.map +1 -0
  298. package/dist/capabilities/pdf-discovery/oaHosts.d.ts +3 -0
  299. package/dist/capabilities/pdf-discovery/oaHosts.d.ts.map +1 -0
  300. package/dist/capabilities/pdf-discovery/oaHosts.js +37 -0
  301. package/dist/capabilities/pdf-discovery/oaHosts.js.map +1 -0
  302. package/dist/capabilities/pdf-discovery/pmcCloud.d.ts +6 -0
  303. package/dist/capabilities/pdf-discovery/pmcCloud.d.ts.map +1 -0
  304. package/dist/capabilities/pdf-discovery/pmcCloud.js +95 -0
  305. package/dist/capabilities/pdf-discovery/pmcCloud.js.map +1 -0
  306. package/dist/capabilities/pdf-discovery/publisherApi/elsevier.d.ts +4 -0
  307. package/dist/capabilities/pdf-discovery/publisherApi/elsevier.d.ts.map +1 -0
  308. package/dist/capabilities/pdf-discovery/publisherApi/elsevier.js +122 -0
  309. package/dist/capabilities/pdf-discovery/publisherApi/elsevier.js.map +1 -0
  310. package/dist/capabilities/pdf-discovery/publisherApi/index.d.ts +31 -0
  311. package/dist/capabilities/pdf-discovery/publisherApi/index.d.ts.map +1 -0
  312. package/dist/capabilities/pdf-discovery/publisherApi/index.js +20 -0
  313. package/dist/capabilities/pdf-discovery/publisherApi/index.js.map +1 -0
  314. package/dist/capabilities/pdf-discovery/publisherApi/springer.d.ts +4 -0
  315. package/dist/capabilities/pdf-discovery/publisherApi/springer.d.ts.map +1 -0
  316. package/dist/capabilities/pdf-discovery/publisherApi/springer.js +114 -0
  317. package/dist/capabilities/pdf-discovery/publisherApi/springer.js.map +1 -0
  318. package/dist/capabilities/pdf-discovery/schemas.d.ts +176 -0
  319. package/dist/capabilities/pdf-discovery/schemas.d.ts.map +1 -0
  320. package/dist/capabilities/pdf-discovery/schemas.js +72 -0
  321. package/dist/capabilities/pdf-discovery/schemas.js.map +1 -0
  322. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.d.ts +5 -0
  323. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.d.ts.map +1 -0
  324. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js +40 -0
  325. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js.map +1 -0
  326. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.d.ts +13 -0
  327. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.d.ts.map +1 -0
  328. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.js +123 -0
  329. package/dist/capabilities/pdf-discovery/tiers/entitledAccess.js.map +1 -0
  330. package/dist/capabilities/pdf-discovery/tiers/primary.d.ts +3 -0
  331. package/dist/capabilities/pdf-discovery/tiers/primary.d.ts.map +1 -0
  332. package/dist/capabilities/pdf-discovery/tiers/primary.js +21 -0
  333. package/dist/capabilities/pdf-discovery/tiers/primary.js.map +1 -0
  334. package/dist/capabilities/pdf-discovery/tiers/publisherApi.d.ts +12 -0
  335. package/dist/capabilities/pdf-discovery/tiers/publisherApi.d.ts.map +1 -0
  336. package/dist/capabilities/pdf-discovery/tiers/publisherApi.js +109 -0
  337. package/dist/capabilities/pdf-discovery/tiers/publisherApi.js.map +1 -0
  338. package/dist/capabilities/pdf-discovery/tiers/repositories.d.ts +10 -0
  339. package/dist/capabilities/pdf-discovery/tiers/repositories.d.ts.map +1 -0
  340. package/dist/capabilities/pdf-discovery/tiers/repositories.js +37 -0
  341. package/dist/capabilities/pdf-discovery/tiers/repositories.js.map +1 -0
  342. package/dist/capabilities/pdf-discovery/tiers/scihub.d.ts +3 -0
  343. package/dist/capabilities/pdf-discovery/tiers/scihub.d.ts.map +1 -0
  344. package/dist/capabilities/pdf-discovery/tiers/scihub.js +21 -0
  345. package/dist/capabilities/pdf-discovery/tiers/scihub.js.map +1 -0
  346. package/dist/capabilities/pdf-discovery/tiers/unpaywall.d.ts +3 -0
  347. package/dist/capabilities/pdf-discovery/tiers/unpaywall.d.ts.map +1 -0
  348. package/dist/capabilities/pdf-discovery/tiers/unpaywall.js +76 -0
  349. package/dist/capabilities/pdf-discovery/tiers/unpaywall.js.map +1 -0
  350. package/dist/capabilities/pdf-discovery/tools.d.ts +337 -0
  351. package/dist/capabilities/pdf-discovery/tools.d.ts.map +1 -0
  352. package/dist/capabilities/pdf-discovery/tools.js +144 -0
  353. package/dist/capabilities/pdf-discovery/tools.js.map +1 -0
  354. package/dist/capabilities/pdf-discovery/types.d.ts +86 -0
  355. package/dist/capabilities/pdf-discovery/types.d.ts.map +1 -0
  356. package/dist/capabilities/pdf-discovery/types.js +2 -0
  357. package/dist/capabilities/pdf-discovery/types.js.map +1 -0
  358. package/dist/cli.d.ts +3 -0
  359. package/dist/cli.d.ts.map +1 -0
  360. package/dist/cli.js +1196 -0
  361. package/dist/cli.js.map +1 -0
  362. package/dist/config/ConfigService.d.ts +28 -0
  363. package/dist/config/ConfigService.d.ts.map +1 -0
  364. package/dist/config/ConfigService.js +204 -0
  365. package/dist/config/ConfigService.js.map +1 -0
  366. package/dist/config/ResultCaps.d.ts +4 -0
  367. package/dist/config/ResultCaps.d.ts.map +1 -0
  368. package/dist/config/ResultCaps.js +10 -0
  369. package/dist/config/ResultCaps.js.map +1 -0
  370. package/dist/config/constants.d.ts +158 -0
  371. package/dist/config/constants.d.ts.map +1 -0
  372. package/dist/config/constants.js +102 -0
  373. package/dist/config/constants.js.map +1 -0
  374. package/dist/core/capabilityProfile.d.ts +2 -0
  375. package/dist/core/capabilityProfile.d.ts.map +1 -0
  376. package/dist/core/capabilityProfile.js +2 -0
  377. package/dist/core/capabilityProfile.js.map +1 -0
  378. package/dist/core/diagnostics.d.ts +2 -0
  379. package/dist/core/diagnostics.d.ts.map +1 -0
  380. package/dist/core/diagnostics.js +2 -0
  381. package/dist/core/diagnostics.js.map +1 -0
  382. package/dist/core/handleToolCall.d.ts +13 -0
  383. package/dist/core/handleToolCall.d.ts.map +1 -0
  384. package/dist/core/handleToolCall.js +304 -0
  385. package/dist/core/handleToolCall.js.map +1 -0
  386. package/dist/core/httpPolicies.d.ts +2 -0
  387. package/dist/core/httpPolicies.d.ts.map +1 -0
  388. package/dist/core/httpPolicies.js +2 -0
  389. package/dist/core/httpPolicies.js.map +1 -0
  390. package/dist/core/liveSmoke.d.ts +2 -0
  391. package/dist/core/liveSmoke.d.ts.map +1 -0
  392. package/dist/core/liveSmoke.js +2 -0
  393. package/dist/core/liveSmoke.js.map +1 -0
  394. package/dist/core/platformFactories.d.ts +2 -0
  395. package/dist/core/platformFactories.d.ts.map +1 -0
  396. package/dist/core/platformFactories.js +2 -0
  397. package/dist/core/platformFactories.js.map +1 -0
  398. package/dist/core/platformMetadata.d.ts +2 -0
  399. package/dist/core/platformMetadata.d.ts.map +1 -0
  400. package/dist/core/platformMetadata.js +2 -0
  401. package/dist/core/platformMetadata.js.map +1 -0
  402. package/dist/core/schemas.d.ts +365 -0
  403. package/dist/core/schemas.d.ts.map +1 -0
  404. package/dist/core/schemas.js +256 -0
  405. package/dist/core/schemas.js.map +1 -0
  406. package/dist/core/searchers.d.ts +41 -0
  407. package/dist/core/searchers.d.ts.map +1 -0
  408. package/dist/core/searchers.js +24 -0
  409. package/dist/core/searchers.js.map +1 -0
  410. package/dist/core/textReports.d.ts +2 -0
  411. package/dist/core/textReports.d.ts.map +1 -0
  412. package/dist/core/textReports.js +2 -0
  413. package/dist/core/textReports.js.map +1 -0
  414. package/dist/core/tools.d.ts +7 -0
  415. package/dist/core/tools.d.ts.map +1 -0
  416. package/dist/core/tools.js +489 -0
  417. package/dist/core/tools.js.map +1 -0
  418. package/dist/infrastructure/cache/RequestCache.d.ts +26 -0
  419. package/dist/infrastructure/cache/RequestCache.d.ts.map +1 -0
  420. package/dist/infrastructure/cache/RequestCache.js +66 -0
  421. package/dist/infrastructure/cache/RequestCache.js.map +1 -0
  422. package/dist/infrastructure/http/ErrorHandler.d.ts +101 -0
  423. package/dist/infrastructure/http/ErrorHandler.d.ts.map +1 -0
  424. package/dist/infrastructure/http/ErrorHandler.js +278 -0
  425. package/dist/infrastructure/http/ErrorHandler.js.map +1 -0
  426. package/dist/infrastructure/http/HttpClient.d.ts +31 -0
  427. package/dist/infrastructure/http/HttpClient.d.ts.map +1 -0
  428. package/dist/infrastructure/http/HttpClient.js +52 -0
  429. package/dist/infrastructure/http/HttpClient.js.map +1 -0
  430. package/dist/infrastructure/pdf/PDFExtractor.d.ts +34 -0
  431. package/dist/infrastructure/pdf/PDFExtractor.d.ts.map +1 -0
  432. package/dist/infrastructure/pdf/PDFExtractor.js +130 -0
  433. package/dist/infrastructure/pdf/PDFExtractor.js.map +1 -0
  434. package/dist/infrastructure/pdf/PdfDownload.d.ts +16 -0
  435. package/dist/infrastructure/pdf/PdfDownload.d.ts.map +1 -0
  436. package/dist/infrastructure/pdf/PdfDownload.js +242 -0
  437. package/dist/infrastructure/pdf/PdfDownload.js.map +1 -0
  438. package/dist/infrastructure/pdf/PdfFirstPage.cjs +70 -0
  439. package/dist/infrastructure/pdf/PdfFirstPage.cjs.map +1 -0
  440. package/dist/infrastructure/pdf/PdfFirstPage.d.cts +16 -0
  441. package/dist/infrastructure/pdf/PdfFirstPage.d.cts.map +1 -0
  442. package/dist/infrastructure/pdf/PdfIdentity.d.ts +29 -0
  443. package/dist/infrastructure/pdf/PdfIdentity.d.ts.map +1 -0
  444. package/dist/infrastructure/pdf/PdfIdentity.js +283 -0
  445. package/dist/infrastructure/pdf/PdfIdentity.js.map +1 -0
  446. package/dist/infrastructure/rate-limit/RateLimiter.d.ts +50 -0
  447. package/dist/infrastructure/rate-limit/RateLimiter.d.ts.map +1 -0
  448. package/dist/infrastructure/rate-limit/RateLimiter.js +128 -0
  449. package/dist/infrastructure/rate-limit/RateLimiter.js.map +1 -0
  450. package/dist/infrastructure/runlog/RunLedger.d.ts +61 -0
  451. package/dist/infrastructure/runlog/RunLedger.d.ts.map +1 -0
  452. package/dist/infrastructure/runlog/RunLedger.js +284 -0
  453. package/dist/infrastructure/runlog/RunLedger.js.map +1 -0
  454. package/dist/infrastructure/runlog/failureClass.d.ts +26 -0
  455. package/dist/infrastructure/runlog/failureClass.d.ts.map +1 -0
  456. package/dist/infrastructure/runlog/failureClass.js +125 -0
  457. package/dist/infrastructure/runlog/failureClass.js.map +1 -0
  458. package/dist/infrastructure/security/SecurityUtils.d.ts +80 -0
  459. package/dist/infrastructure/security/SecurityUtils.d.ts.map +1 -0
  460. package/dist/infrastructure/security/SecurityUtils.js +357 -0
  461. package/dist/infrastructure/security/SecurityUtils.js.map +1 -0
  462. package/dist/management/capability-profile/carsiReadiness.d.ts +10 -0
  463. package/dist/management/capability-profile/carsiReadiness.d.ts.map +1 -0
  464. package/dist/management/capability-profile/carsiReadiness.js +13 -0
  465. package/dist/management/capability-profile/carsiReadiness.js.map +1 -0
  466. package/dist/management/capability-profile/index.d.ts +26 -0
  467. package/dist/management/capability-profile/index.d.ts.map +1 -0
  468. package/dist/management/capability-profile/index.js +183 -0
  469. package/dist/management/capability-profile/index.js.map +1 -0
  470. package/dist/management/carsi/host.d.ts +4 -0
  471. package/dist/management/carsi/host.d.ts.map +1 -0
  472. package/dist/management/carsi/host.js +65 -0
  473. package/dist/management/carsi/host.js.map +1 -0
  474. package/dist/management/carsi/installer.d.ts +146 -0
  475. package/dist/management/carsi/installer.d.ts.map +1 -0
  476. package/dist/management/carsi/installer.js +527 -0
  477. package/dist/management/carsi/installer.js.map +1 -0
  478. package/dist/management/carsi/runtime.d.ts +31 -0
  479. package/dist/management/carsi/runtime.d.ts.map +1 -0
  480. package/dist/management/carsi/runtime.js +67 -0
  481. package/dist/management/carsi/runtime.js.map +1 -0
  482. package/dist/management/config/index.d.ts +2 -0
  483. package/dist/management/config/index.d.ts.map +1 -0
  484. package/dist/management/config/index.js +2 -0
  485. package/dist/management/config/index.js.map +1 -0
  486. package/dist/management/diagnostics/index.d.ts +43 -0
  487. package/dist/management/diagnostics/index.d.ts.map +1 -0
  488. package/dist/management/diagnostics/index.js +610 -0
  489. package/dist/management/diagnostics/index.js.map +1 -0
  490. package/dist/management/failures/index.d.ts +47 -0
  491. package/dist/management/failures/index.d.ts.map +1 -0
  492. package/dist/management/failures/index.js +134 -0
  493. package/dist/management/failures/index.js.map +1 -0
  494. package/dist/management/reports/index.d.ts +32 -0
  495. package/dist/management/reports/index.d.ts.map +1 -0
  496. package/dist/management/reports/index.js +123 -0
  497. package/dist/management/reports/index.js.map +1 -0
  498. package/dist/management/skills/autoSync.d.ts +41 -0
  499. package/dist/management/skills/autoSync.d.ts.map +1 -0
  500. package/dist/management/skills/autoSync.js +142 -0
  501. package/dist/management/skills/autoSync.js.map +1 -0
  502. package/dist/management/skills/index.d.ts +3 -0
  503. package/dist/management/skills/index.d.ts.map +1 -0
  504. package/dist/management/skills/index.js +3 -0
  505. package/dist/management/skills/index.js.map +1 -0
  506. package/dist/management/smoke/index.d.ts +44 -0
  507. package/dist/management/smoke/index.d.ts.map +1 -0
  508. package/dist/management/smoke/index.js +254 -0
  509. package/dist/management/smoke/index.js.map +1 -0
  510. package/dist/models/Paper.d.ts +64 -0
  511. package/dist/models/Paper.d.ts.map +1 -0
  512. package/dist/models/Paper.js +70 -0
  513. package/dist/models/Paper.js.map +1 -0
  514. package/dist/platforms/ACMSearcher.d.ts +16 -0
  515. package/dist/platforms/ACMSearcher.d.ts.map +1 -0
  516. package/dist/platforms/ACMSearcher.js +125 -0
  517. package/dist/platforms/ACMSearcher.js.map +1 -0
  518. package/dist/platforms/BioRxivSearcher.d.ts +47 -0
  519. package/dist/platforms/BioRxivSearcher.d.ts.map +1 -0
  520. package/dist/platforms/BioRxivSearcher.js +215 -0
  521. package/dist/platforms/BioRxivSearcher.js.map +1 -0
  522. package/dist/platforms/CORESearcher.d.ts +17 -0
  523. package/dist/platforms/CORESearcher.d.ts.map +1 -0
  524. package/dist/platforms/CORESearcher.js +185 -0
  525. package/dist/platforms/CORESearcher.js.map +1 -0
  526. package/dist/platforms/CrossrefSearcher.d.ts +34 -0
  527. package/dist/platforms/CrossrefSearcher.d.ts.map +1 -0
  528. package/dist/platforms/CrossrefSearcher.js +339 -0
  529. package/dist/platforms/CrossrefSearcher.js.map +1 -0
  530. package/dist/platforms/DBLPSearcher.d.ts +16 -0
  531. package/dist/platforms/DBLPSearcher.d.ts.map +1 -0
  532. package/dist/platforms/DBLPSearcher.js +116 -0
  533. package/dist/platforms/DBLPSearcher.js.map +1 -0
  534. package/dist/platforms/EuropePMCSearcher.d.ts +20 -0
  535. package/dist/platforms/EuropePMCSearcher.d.ts.map +1 -0
  536. package/dist/platforms/EuropePMCSearcher.js +173 -0
  537. package/dist/platforms/EuropePMCSearcher.js.map +1 -0
  538. package/dist/platforms/GoogleScholarSearcher.d.ts +142 -0
  539. package/dist/platforms/GoogleScholarSearcher.d.ts.map +1 -0
  540. package/dist/platforms/GoogleScholarSearcher.js +648 -0
  541. package/dist/platforms/GoogleScholarSearcher.js.map +1 -0
  542. package/dist/platforms/IACRSearcher.d.ts +51 -0
  543. package/dist/platforms/IACRSearcher.d.ts.map +1 -0
  544. package/dist/platforms/IACRSearcher.js +339 -0
  545. package/dist/platforms/IACRSearcher.js.map +1 -0
  546. package/dist/platforms/IEEESearcher.d.ts +15 -0
  547. package/dist/platforms/IEEESearcher.d.ts.map +1 -0
  548. package/dist/platforms/IEEESearcher.js +123 -0
  549. package/dist/platforms/IEEESearcher.js.map +1 -0
  550. package/dist/platforms/OpenAIRESearcher.d.ts +22 -0
  551. package/dist/platforms/OpenAIRESearcher.d.ts.map +1 -0
  552. package/dist/platforms/OpenAIRESearcher.js +235 -0
  553. package/dist/platforms/OpenAIRESearcher.js.map +1 -0
  554. package/dist/platforms/OpenAlexSearcher.d.ts +14 -0
  555. package/dist/platforms/OpenAlexSearcher.d.ts.map +1 -0
  556. package/dist/platforms/OpenAlexSearcher.js +114 -0
  557. package/dist/platforms/OpenAlexSearcher.js.map +1 -0
  558. package/dist/platforms/OpenReviewSearcher.d.ts +19 -0
  559. package/dist/platforms/OpenReviewSearcher.d.ts.map +1 -0
  560. package/dist/platforms/OpenReviewSearcher.js +141 -0
  561. package/dist/platforms/OpenReviewSearcher.js.map +1 -0
  562. package/dist/platforms/PMCSearcher.d.ts +20 -0
  563. package/dist/platforms/PMCSearcher.d.ts.map +1 -0
  564. package/dist/platforms/PMCSearcher.js +177 -0
  565. package/dist/platforms/PMCSearcher.js.map +1 -0
  566. package/dist/platforms/PaperSource.d.ts +149 -0
  567. package/dist/platforms/PaperSource.d.ts.map +1 -0
  568. package/dist/platforms/PaperSource.js +125 -0
  569. package/dist/platforms/PaperSource.js.map +1 -0
  570. package/dist/platforms/PubMedSearcher.d.ts +104 -0
  571. package/dist/platforms/PubMedSearcher.d.ts.map +1 -0
  572. package/dist/platforms/PubMedSearcher.js +430 -0
  573. package/dist/platforms/PubMedSearcher.js.map +1 -0
  574. package/dist/platforms/SciHubSearcher.d.ts +66 -0
  575. package/dist/platforms/SciHubSearcher.d.ts.map +1 -0
  576. package/dist/platforms/SciHubSearcher.js +398 -0
  577. package/dist/platforms/SciHubSearcher.js.map +1 -0
  578. package/dist/platforms/ScienceDirectSearcher.d.ts +42 -0
  579. package/dist/platforms/ScienceDirectSearcher.d.ts.map +1 -0
  580. package/dist/platforms/ScienceDirectSearcher.js +334 -0
  581. package/dist/platforms/ScienceDirectSearcher.js.map +1 -0
  582. package/dist/platforms/ScopusSearcher.d.ts +43 -0
  583. package/dist/platforms/ScopusSearcher.d.ts.map +1 -0
  584. package/dist/platforms/ScopusSearcher.js +377 -0
  585. package/dist/platforms/ScopusSearcher.js.map +1 -0
  586. package/dist/platforms/SemanticScholarSearcher.d.ts +96 -0
  587. package/dist/platforms/SemanticScholarSearcher.d.ts.map +1 -0
  588. package/dist/platforms/SemanticScholarSearcher.js +419 -0
  589. package/dist/platforms/SemanticScholarSearcher.js.map +1 -0
  590. package/dist/platforms/SpringerSearcher.d.ts +54 -0
  591. package/dist/platforms/SpringerSearcher.d.ts.map +1 -0
  592. package/dist/platforms/SpringerSearcher.js +387 -0
  593. package/dist/platforms/SpringerSearcher.js.map +1 -0
  594. package/dist/platforms/USENIXSearcher.d.ts +14 -0
  595. package/dist/platforms/USENIXSearcher.d.ts.map +1 -0
  596. package/dist/platforms/USENIXSearcher.js +75 -0
  597. package/dist/platforms/USENIXSearcher.js.map +1 -0
  598. package/dist/platforms/UnpaywallSearcher.d.ts +29 -0
  599. package/dist/platforms/UnpaywallSearcher.d.ts.map +1 -0
  600. package/dist/platforms/UnpaywallSearcher.js +135 -0
  601. package/dist/platforms/UnpaywallSearcher.js.map +1 -0
  602. package/dist/platforms/WebOfScienceSearcher.d.ts +111 -0
  603. package/dist/platforms/WebOfScienceSearcher.d.ts.map +1 -0
  604. package/dist/platforms/WebOfScienceSearcher.js +500 -0
  605. package/dist/platforms/WebOfScienceSearcher.js.map +1 -0
  606. package/dist/registry/aliases.d.ts +2 -0
  607. package/dist/registry/aliases.d.ts.map +1 -0
  608. package/dist/registry/aliases.js +2 -0
  609. package/dist/registry/aliases.js.map +1 -0
  610. package/dist/registry/httpPolicies.d.ts +3 -0
  611. package/dist/registry/httpPolicies.d.ts.map +1 -0
  612. package/dist/registry/httpPolicies.js +13 -0
  613. package/dist/registry/httpPolicies.js.map +1 -0
  614. package/dist/registry/platformFactories.d.ts +8 -0
  615. package/dist/registry/platformFactories.d.ts.map +1 -0
  616. package/dist/registry/platformFactories.js +51 -0
  617. package/dist/registry/platformFactories.js.map +1 -0
  618. package/dist/registry/platformMetadata.d.ts +55 -0
  619. package/dist/registry/platformMetadata.d.ts.map +1 -0
  620. package/dist/registry/platformMetadata.js +438 -0
  621. package/dist/registry/platformMetadata.js.map +1 -0
  622. package/dist/services/CitationService.d.ts +3 -0
  623. package/dist/services/CitationService.d.ts.map +1 -0
  624. package/dist/services/CitationService.js +2 -0
  625. package/dist/services/CitationService.js.map +1 -0
  626. package/dist/services/JournalMetricsService.d.ts +3 -0
  627. package/dist/services/JournalMetricsService.d.ts.map +1 -0
  628. package/dist/services/JournalMetricsService.js +2 -0
  629. package/dist/services/JournalMetricsService.js.map +1 -0
  630. package/dist/services/MultiSourceSearchService.d.ts +3 -0
  631. package/dist/services/MultiSourceSearchService.d.ts.map +1 -0
  632. package/dist/services/MultiSourceSearchService.js +2 -0
  633. package/dist/services/MultiSourceSearchService.js.map +1 -0
  634. package/dist/services/OpenAccessFallbackService.d.ts +4 -0
  635. package/dist/services/OpenAccessFallbackService.d.ts.map +1 -0
  636. package/dist/services/OpenAccessFallbackService.js +2 -0
  637. package/dist/services/OpenAccessFallbackService.js.map +1 -0
  638. package/dist/skills/SkillInstaller.d.ts +110 -0
  639. package/dist/skills/SkillInstaller.d.ts.map +1 -0
  640. package/dist/skills/SkillInstaller.js +393 -0
  641. package/dist/skills/SkillInstaller.js.map +1 -0
  642. package/dist/utils/ErrorHandler.d.ts +3 -0
  643. package/dist/utils/ErrorHandler.d.ts.map +1 -0
  644. package/dist/utils/ErrorHandler.js +3 -0
  645. package/dist/utils/ErrorHandler.js.map +1 -0
  646. package/dist/utils/HttpClient.d.ts +2 -0
  647. package/dist/utils/HttpClient.d.ts.map +1 -0
  648. package/dist/utils/HttpClient.js +2 -0
  649. package/dist/utils/HttpClient.js.map +1 -0
  650. package/dist/utils/Logger.d.ts +6 -0
  651. package/dist/utils/Logger.d.ts.map +1 -0
  652. package/dist/utils/Logger.js +26 -0
  653. package/dist/utils/Logger.js.map +1 -0
  654. package/dist/utils/PDFExtractor.d.ts +3 -0
  655. package/dist/utils/PDFExtractor.d.ts.map +1 -0
  656. package/dist/utils/PDFExtractor.js +3 -0
  657. package/dist/utils/PDFExtractor.js.map +1 -0
  658. package/dist/utils/PdfDownload.d.ts +2 -0
  659. package/dist/utils/PdfDownload.d.ts.map +1 -0
  660. package/dist/utils/PdfDownload.js +2 -0
  661. package/dist/utils/PdfDownload.js.map +1 -0
  662. package/dist/utils/QuotaManager.d.ts +32 -0
  663. package/dist/utils/QuotaManager.d.ts.map +1 -0
  664. package/dist/utils/QuotaManager.js +95 -0
  665. package/dist/utils/QuotaManager.js.map +1 -0
  666. package/dist/utils/RateLimiter.d.ts +2 -0
  667. package/dist/utils/RateLimiter.d.ts.map +1 -0
  668. package/dist/utils/RateLimiter.js +2 -0
  669. package/dist/utils/RateLimiter.js.map +1 -0
  670. package/dist/utils/RequestCache.d.ts +3 -0
  671. package/dist/utils/RequestCache.d.ts.map +1 -0
  672. package/dist/utils/RequestCache.js +3 -0
  673. package/dist/utils/RequestCache.js.map +1 -0
  674. package/dist/utils/SecurityUtils.d.ts +3 -0
  675. package/dist/utils/SecurityUtils.d.ts.map +1 -0
  676. package/dist/utils/SecurityUtils.js +3 -0
  677. package/dist/utils/SecurityUtils.js.map +1 -0
  678. package/package.json +118 -4
  679. package/skills/paper-search/SKILL.md +151 -0
  680. package/skills/paper-search/references/browser-download.md +101 -0
  681. package/skills/paper-search/references/capability-routing.md +174 -0
  682. package/skills/paper-search/references/carsi-setup.md +69 -0
  683. package/skills/paper-search/references/cli-contract.md +200 -0
  684. package/skills/paper-search/references/management-layer.md +149 -0
  685. package/skills/paper-search/references/publisher-support.md +72 -0
  686. package/skills/paper-search/references/troubleshooting.md +15 -0
@@ -0,0 +1,530 @@
1
+ """Task progress reporting via a well-known JSON file.
2
+
3
+ Writers (server / batch) report per-paper progress here; readers (the
4
+ floating progress window, ``paper-search-carsi progress``) watch the file. The two
5
+ are decoupled on purpose: a headless background task stays GUI-free while the
6
+ floating bar makes its progress visible on the desktop.
7
+
8
+ File: <data_dir>/progress/current.json — atomically replaced (tmp + rename)
9
+ on every update, so readers never see a torn write.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import os
16
+ import tempfile
17
+ import threading
18
+ import time
19
+ import uuid
20
+ from contextlib import contextmanager
21
+ from dataclasses import dataclass
22
+ from contextvars import ContextVar
23
+ from datetime import datetime
24
+ from pathlib import Path
25
+ from typing import Any, Callable
26
+
27
+ _LOCK = threading.Lock()
28
+ _STATE: dict[str, Any] = {}
29
+ _STARTED_MONO: float = 0.0
30
+ _STAGE_LISTENER: ContextVar[tuple[str, Callable[[str], None]] | None] = ContextVar(
31
+ "carsi_request_stage_listener", default=None)
32
+
33
+ STAGES = frozenset({
34
+ "starting", "opening_article", "waiting_login", "waiting_verification",
35
+ "checking_access", "fetching_pdf", "pdf_captured",
36
+ "saved_pending_verification", "route_failed", "failed", "cancelled", "interrupted", "unknown",
37
+ })
38
+ _IDENTITY_FIELDS = frozenset({"schema_version", "run_id", "revision", "owner_pid"})
39
+
40
+ STALE_SECONDS = 120 # legacy reports expire; a living owner's stalled task stays visible
41
+ # Between two items of a serial batch no backend process owns the task; the
42
+ # caller is running its own (open-access) stages. Give up after this long.
43
+ BATCH_WAIT_STALE_SECONDS = 1800
44
+
45
+
46
+ def _progress_dir() -> Path:
47
+ data_dir = Path(os.environ.get("CARSI_DATA_DIR", str(Path.home() / ".local" / "share" / "paper-search-cli" / "carsi")))
48
+ return data_dir / "progress"
49
+
50
+
51
+ def progress_path() -> Path:
52
+ return _progress_dir() / "current.json"
53
+
54
+
55
+ def _atomic_write(payload: dict[str, Any]) -> None:
56
+ path = progress_path()
57
+ path.parent.mkdir(parents=True, exist_ok=True)
58
+ fd, tmp = tempfile.mkstemp(dir=str(path.parent), suffix=".tmp")
59
+ try:
60
+ with os.fdopen(fd, "w", encoding="utf-8") as fh:
61
+ json.dump(payload, fh, ensure_ascii=False, indent=2)
62
+ os.replace(tmp, path)
63
+ except Exception:
64
+ try:
65
+ os.unlink(tmp)
66
+ except OSError:
67
+ pass
68
+ raise
69
+
70
+
71
+ def _pid_alive(pid: int) -> bool:
72
+ """Windows-safe liveness probe. NEVER use os.kill(pid, 0) on Windows —
73
+ any signal value there calls TerminateProcess and would murder the bar."""
74
+ if pid <= 0:
75
+ return False
76
+ try:
77
+ if os.name != "nt":
78
+ os.kill(pid, 0)
79
+ return True
80
+ import ctypes
81
+
82
+ SYNCHRONIZE = 0x00100000
83
+ kernel32 = ctypes.windll.kernel32
84
+ handle = kernel32.OpenProcess(SYNCHRONIZE, False, int(pid))
85
+ if not handle:
86
+ return False
87
+ kernel32.CloseHandle(int(handle))
88
+ return True
89
+ except Exception:
90
+ return False
91
+
92
+
93
+ def ensure_progress_bar(config: dict[str, Any] | None = None) -> None:
94
+ """Spawn the floating progress window once (no-op when one is already up).
95
+
96
+ Called whenever a download task starts so the user sees progress without
97
+ launching anything by hand; PaperSearch additionally prints the same
98
+ stages to stderr. The window is an independent process guarded by
99
+ ``progress/bar.pid``; a stale lock (dead window) is reclaimed.
100
+
101
+ Disabled by config ``progress_bar_auto: false``, env
102
+ ``CARSI_PROGRESS_BAR=0``, and always under pytest so test runs never
103
+ open windows.
104
+ """
105
+ if os.environ.get("CARSI_PROGRESS_BAR") == "0":
106
+ return
107
+ if os.environ.get("CARSI_PROGRESS_BAR_CHILD") == "1":
108
+ return
109
+ if "PYTEST_CURRENT_TEST" in os.environ:
110
+ return
111
+ if config is not None and config.get("progress_bar_auto") is False:
112
+ return
113
+ lock = _progress_dir() / "bar.pid"
114
+ try:
115
+ if lock.exists():
116
+ pid = int(lock.read_text(encoding="utf-8").strip() or 0)
117
+ if _pid_alive(pid):
118
+ return # a window is already up
119
+ # else: stale lock (window died) - reclaim below
120
+ except (ValueError, OSError):
121
+ return
122
+ try:
123
+ import subprocess
124
+ import sys
125
+
126
+ kwargs = {"creationflags": 0x08000000} if os.name == "nt" else {}
127
+ env = {**os.environ, "CARSI_PROGRESS_BAR_CHILD": "1"}
128
+ subprocess.Popen(
129
+ [sys.executable, "-m", "paper_search_carsi.progress_bar"],
130
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
131
+ env=env, start_new_session=(os.name != "nt"), **kwargs,
132
+ )
133
+ except Exception:
134
+ pass
135
+
136
+
137
+ @dataclass(frozen=True)
138
+ class BatchContext:
139
+ """Position of one download inside a caller's serial batch.
140
+
141
+ The counts are the caller's totals *before* this item, so items the caller
142
+ finished without the backend (open access, Sci-Hub) are still counted.
143
+ """
144
+
145
+ batch_id: str
146
+ index: int
147
+ total: int
148
+ done: int | None = None
149
+ succeeded: int | None = None
150
+ failed: int | None = None
151
+
152
+ @classmethod
153
+ def from_args(cls, batch_id: Any, index: Any, total: Any, done: Any = None,
154
+ succeeded: Any = None, failed: Any = None) -> "BatchContext | None":
155
+ """Validated context, or None (then the download is reported on its own)."""
156
+ def count(value: Any) -> int | None:
157
+ return value if isinstance(value, int) and not isinstance(value, bool) and value >= 0 else None
158
+
159
+ if not isinstance(batch_id, str) or not batch_id.strip() or len(batch_id) > 128:
160
+ return None
161
+ if count(index) is None or count(total) is None or not 1 <= index <= total <= 100_000:
162
+ return None
163
+ counts = [count(v) for v in (done, succeeded, failed)]
164
+ return cls(batch_id.strip(), index, total, *counts)
165
+
166
+
167
+ def _batch_state(batch_id: str) -> dict[str, Any] | None:
168
+ """The running report of this batch, if the previous item left one."""
169
+ try:
170
+ state = json.loads(progress_path().read_text(encoding="utf-8"))
171
+ except Exception:
172
+ return None
173
+ if isinstance(state, dict) and state.get("batch_id") == batch_id and state.get("status") == "running":
174
+ return state
175
+ return None
176
+
177
+
178
+ def _adopt_batch(batch_id: str, total: int, counts: tuple[int | None, int | None, int | None],
179
+ **fields: Any) -> None:
180
+ """Continue this batch's report in the current process, or start it."""
181
+ global _STATE, _STARTED_MONO
182
+ try:
183
+ from .config import load_config
184
+
185
+ ensure_progress_bar(load_config())
186
+ except Exception:
187
+ try:
188
+ ensure_progress_bar()
189
+ except Exception:
190
+ pass
191
+ previous = _batch_state(batch_id)
192
+ with _LOCK:
193
+ _STARTED_MONO = time.monotonic()
194
+ if previous is None:
195
+ previous = {
196
+ "task": "机构 PDF 批量下载",
197
+ "done": 0, "success": 0, "failed": 0,
198
+ "started_at": datetime.now().isoformat(timespec="seconds"),
199
+ "schema_version": 1,
200
+ "run_id": str(uuid.uuid4()),
201
+ "revision": 0,
202
+ }
203
+ done, succeeded, failed = counts
204
+ _STATE = {
205
+ **previous,
206
+ "batch_id": batch_id,
207
+ "total": int(total),
208
+ "status": "running",
209
+ "awaiting_next": False,
210
+ "attention": {},
211
+ "owner_pid": os.getpid(),
212
+ **({"done": done} if done is not None else {}),
213
+ **({"success": succeeded} if succeeded is not None else {}),
214
+ **({"failed": failed} if failed is not None else {}),
215
+ **fields,
216
+ }
217
+ _STATE.pop("finished_at", None)
218
+ _write_state()
219
+
220
+
221
+ def sync_batch(batch_id: str, total: int, done: int, succeeded: int, failed: int, *,
222
+ current: str = "", finished: bool = False) -> bool:
223
+ """Record batch totals for items finished without the backend, or the batch end."""
224
+ ctx = BatchContext.from_args(batch_id, max(1, min(done, total)), total, done, succeeded, failed)
225
+ if ctx is None or done > total:
226
+ return False
227
+ previous = _batch_state(ctx.batch_id)
228
+ if finished and previous is None and done == 0:
229
+ return False
230
+ _adopt_batch(ctx.batch_id, total, (done, succeeded, failed),
231
+ **({"current": current} if current else {}),
232
+ stage="saved_pending_verification" if finished else "starting",
233
+ phase=(f"批量下载结束:成功 {succeeded},失败 {failed}" if finished
234
+ else f"已完成 {done}/{total} 篇,等待下一篇"))
235
+ with _LOCK:
236
+ if finished:
237
+ _STATE.update(status="done", awaiting_next=False,
238
+ finished_at=datetime.now().isoformat(timespec="seconds"))
239
+ else:
240
+ _STATE.update(awaiting_next=True)
241
+ _write_state()
242
+ return True
243
+
244
+
245
+ @contextmanager
246
+ def tracked_download(current: str, output_dir: Any = None, batch: BatchContext | None = None,
247
+ content: str = "main"):
248
+ """One institutional download as a reported task (progress window + live preview).
249
+
250
+ Every single-paper entry point (MCP tool, ``publisher-check``) runs inside
251
+ this, so the floating window and the browser preview start the same way.
252
+ The caller stores the result dict under ``outcome["result"]`` and may set
253
+ ``outcome["failure_phase"]``. Inside a serial batch the item continues the
254
+ batch's report (``k / N``) and the window stays open between items.
255
+ ``content`` (main / supplements / both) is shown as a small label.
256
+ ``outcome["paused"]`` (a phase text) means the article has not run yet and
257
+ the batch tries it again later (publisher's hourly limit): nothing is
258
+ counted and the window stays open.
259
+ """
260
+ if batch is None:
261
+ start_task("机构 PDF 下载", total=1, current=current, phase="进入机构访问")
262
+ else:
263
+ done = batch.done if batch.done is not None else (None if _batch_state(batch.batch_id) else batch.index - 1)
264
+ _adopt_batch(batch.batch_id, batch.total, (done, batch.succeeded, batch.failed),
265
+ current=current, stage="starting",
266
+ phase=f"第 {batch.index}/{batch.total} 篇:进入机构访问")
267
+ _notify_stage("starting", current)
268
+ update(content=content if content in ("main", "supplements", "both") else "main")
269
+ if output_dir:
270
+ set_output_dir(output_dir)
271
+ outcome: dict[str, Any] = {}
272
+ try:
273
+ yield outcome
274
+ finally:
275
+ if outcome.get("paused") and batch is not None:
276
+ with _LOCK:
277
+ if _STATE:
278
+ _STATE.update(awaiting_next=True, phase=str(outcome["paused"]))
279
+ _write_state()
280
+ else:
281
+ _finish_download(outcome, current, batch)
282
+
283
+
284
+ def _finish_download(outcome: dict[str, Any], current: str, batch: BatchContext | None) -> None:
285
+ """Count the download and show how it ended (end of ``tracked_download``)."""
286
+ succeeded = (outcome.get("result") or {}).get("success") is True
287
+ phase = "PDF 已保存,待内容核验" if succeeded else outcome.get("failure_phase", "下载未完成")
288
+ report_stage("saved_pending_verification" if succeeded else "failed", current=current)
289
+ advance(succeeded, current=current, phase=phase)
290
+ if batch is not None and batch.index < batch.total:
291
+ with _LOCK:
292
+ if _STATE:
293
+ _STATE.update(awaiting_next=True,
294
+ phase=f"第 {batch.index}/{batch.total} 篇{'已保存' if succeeded else '未完成'},等待下一篇")
295
+ _write_state()
296
+ else:
297
+ finish()
298
+
299
+
300
+ def _write_state() -> None:
301
+ """Called under _LOCK; revisions count successful atomic replacements only."""
302
+ payload = {
303
+ **_STATE,
304
+ "updated_at": datetime.now().isoformat(timespec="seconds"),
305
+ "revision": _STATE.get("revision", 0) + 1,
306
+ }
307
+ try:
308
+ _atomic_write(payload)
309
+ except Exception:
310
+ # Progress is best-effort; never fail the download because of its UI.
311
+ return
312
+ _STATE.update(payload)
313
+
314
+
315
+ @contextmanager
316
+ def listen_for_stages(identifier: str, listener: Callable[[str], None]):
317
+ """Bind live events to this operation, without consulting shared GUI state.
318
+
319
+ asyncio.to_thread copies this context. Unbound source-pool threads are
320
+ deliberately excluded; their caller must explicitly propagate the context.
321
+ """
322
+ from .identifiers import normalize_doi
323
+
324
+ token = _STAGE_LISTENER.set((normalize_doi(identifier).lower(), listener))
325
+ try:
326
+ yield
327
+ finally:
328
+ _STAGE_LISTENER.reset(token)
329
+
330
+
331
+ def _notify_stage(stage: str, current: str = "") -> None:
332
+ bound = _STAGE_LISTENER.get()
333
+ if bound is None:
334
+ return
335
+ from .identifiers import normalize_doi
336
+
337
+ identifier, listener = bound
338
+ if current and normalize_doi(current).lower() != identifier:
339
+ return
340
+ # Optional observers cannot turn a download into a failure. Never log
341
+ # observer exceptions, which may carry transport or authentication data.
342
+ try:
343
+ listener(stage)
344
+ except Exception:
345
+ pass
346
+
347
+
348
+ def report_stage(stage: str, *, current: str = "") -> None:
349
+ """Report an explicit machine stage, independently of GUI prose.
350
+
351
+ saved_pending_verification means bytes were saved, not that the product
352
+ verified the article's identity. route_failed leaves the task running for
353
+ other routes. No stage is inferred from phase text.
354
+ """
355
+ if not isinstance(stage, str) or stage not in STAGES:
356
+ raise ValueError("invalid progress stage")
357
+ # Stage-only callers (e.g. a wait on the user's login) must retain the DOI
358
+ # already bound by start_task; otherwise filtered monitors lose the run.
359
+ update(stage=stage, **({"current": current} if current else {}))
360
+ _notify_stage(stage, current)
361
+
362
+
363
+ def start_task(task: str, total: int, **extra: Any) -> None:
364
+ """Begin (or restart) the reported task with a known total."""
365
+ global _STATE, _STARTED_MONO
366
+ try:
367
+ from .config import load_config
368
+
369
+ ensure_progress_bar(load_config())
370
+ except Exception:
371
+ try:
372
+ ensure_progress_bar()
373
+ except Exception:
374
+ pass
375
+ with _LOCK:
376
+ _STARTED_MONO = time.monotonic()
377
+ _STATE = {
378
+ "task": task,
379
+ "total": int(total),
380
+ "done": 0,
381
+ "success": 0,
382
+ "failed": 0,
383
+ "current": "",
384
+ "phase": "",
385
+ "status": "running",
386
+ "attention": {},
387
+ "started_at": datetime.now().isoformat(timespec="seconds"),
388
+ **extra,
389
+ "schema_version": 1,
390
+ "run_id": str(uuid.uuid4()),
391
+ "revision": 0,
392
+ "owner_pid": os.getpid(),
393
+ "stage": "starting",
394
+ }
395
+ _write_state()
396
+
397
+ _notify_stage("starting", str(extra.get("current", "")))
398
+
399
+
400
+ def update(**kw: Any) -> None:
401
+ """Merge fields into the current report (done/success/failed/current/...)."""
402
+ if _IDENTITY_FIELDS.intersection(kw):
403
+ raise ValueError("progress identity fields are managed by start_task")
404
+ if "stage" in kw and (not isinstance(kw["stage"], str) or kw["stage"] not in STAGES):
405
+ raise ValueError("invalid progress stage")
406
+ with _LOCK:
407
+ if not _STATE:
408
+ return
409
+ _STATE.update(kw)
410
+ _write_state()
411
+
412
+
413
+ def advance(ok: bool, current: str = "", phase: str = "") -> None:
414
+ """Record one finished item (the hot path for per-paper lanes)."""
415
+ with _LOCK:
416
+ if not _STATE:
417
+ return
418
+ _STATE["done"] = int(_STATE.get("done", 0)) + 1
419
+ _STATE["success"] = int(_STATE.get("success", 0)) + (1 if ok else 0)
420
+ _STATE["failed"] = int(_STATE.get("failed", 0)) + (0 if ok else 1)
421
+ if current:
422
+ _STATE["current"] = current
423
+ if phase:
424
+ _STATE["phase"] = phase
425
+ _write_state()
426
+
427
+
428
+ def set_attention(key: str, message: str, *, current: str = "", phase: str = "") -> None:
429
+ """Show a manual-action reminder without stopping other workers."""
430
+ with _LOCK:
431
+ if not _STATE:
432
+ return
433
+ attention = _STATE.setdefault("attention", {})
434
+ if not isinstance(attention, dict):
435
+ attention = {}
436
+ _STATE["attention"] = attention
437
+ attention[str(key)] = {
438
+ "message": str(message),
439
+ "current": str(current or ""),
440
+ "phase": str(phase or ""),
441
+ "since": datetime.now().isoformat(timespec="seconds"),
442
+ }
443
+ _write_state()
444
+
445
+
446
+ def set_output_dir(path) -> None:
447
+ """Record the output folder for task diagnostics; omit it from the widget."""
448
+ with _LOCK:
449
+ if not _STATE:
450
+ return
451
+ _STATE["output_dir"] = str(path)
452
+ _write_state()
453
+
454
+
455
+ def clear_attention(key: str) -> None:
456
+ """Remove one manual-action reminder after its browser page clears."""
457
+ with _LOCK:
458
+ if not _STATE:
459
+ return
460
+ attention = _STATE.get("attention")
461
+ if not isinstance(attention, dict):
462
+ return
463
+ attention.pop(str(key), None)
464
+ _write_state()
465
+
466
+
467
+ def finish() -> None:
468
+ """Mark the task done (the bar keeps the final state briefly, then idles)."""
469
+ with _LOCK:
470
+ if not _STATE:
471
+ return
472
+ _STATE["status"] = "done"
473
+ _STATE["awaiting_next"] = False
474
+ _STATE["attention"] = {}
475
+ _STATE["finished_at"] = datetime.now().isoformat(timespec="seconds")
476
+ _write_state()
477
+
478
+
479
+ def elapsed_sec() -> float:
480
+ return max(0.0, time.monotonic() - _STARTED_MONO)
481
+
482
+
483
+ def read_snapshot() -> tuple[dict[str, Any] | None, str]:
484
+ """Read once without writes/config/GUI; reason codes contain no raw errors."""
485
+ try:
486
+ raw = progress_path().read_text(encoding="utf-8")
487
+ state = json.loads(raw)
488
+ except FileNotFoundError:
489
+ return None, "missing"
490
+ except (ValueError, RecursionError):
491
+ # Includes malformed UTF-8/JSON and Python's oversized-int guard.
492
+ return None, "invalid_json"
493
+ except OSError:
494
+ return None, "unreadable"
495
+ if not isinstance(state, dict):
496
+ return None, "invalid_state"
497
+ if state.get("status") == "running" and state.get("awaiting_next") is True:
498
+ # Between batch items no process owns the task; only time ends the wait.
499
+ try:
500
+ ts = datetime.fromisoformat(str(state.get("updated_at")))
501
+ waited = (datetime.now(ts.tzinfo) - ts).total_seconds()
502
+ except Exception:
503
+ waited = 0.0
504
+ if waited > BATCH_WAIT_STALE_SECONDS:
505
+ return {**state, "status": "interrupted", "stage": "interrupted", "awaiting_next": False,
506
+ "phase": "批量下载已停止,结果未确认", "attention": {}}, "available"
507
+ return state, "available"
508
+ if state.get("status") == "running" and isinstance(state.get("owner_pid"), int) and not _pid_alive(state["owner_pid"]):
509
+ # The MCP process can be terminated before its finally block runs.
510
+ # Do not claim success, failure, or cancellation without a result.
511
+ return {**state, "status": "interrupted", "stage": "interrupted", "phase": "下载进程已结束,结果未确认", "attention": {}}, "available"
512
+ if state.get("status") in ("done", "cancelled", "interrupted"):
513
+ return state, "available" # finished states stay visible (reader decides)
514
+ try:
515
+ ts = datetime.fromisoformat(str(state.get("updated_at")))
516
+ age = (datetime.now(ts.tzinfo) - ts).total_seconds()
517
+ if age > STALE_SECONDS:
518
+ if state.get("status") == "running" and isinstance(state.get("owner_pid"), int):
519
+ # The owner was checked above. A long blocking browser call
520
+ # must not make an active download disappear from the bar.
521
+ return {**state, "phase": "等待下载进程响应(暂无新进度)", "stale": True}, "available"
522
+ return None, "stale"
523
+ except Exception:
524
+ pass
525
+ return state, "available"
526
+
527
+
528
+ def read_state() -> dict[str, Any] | None:
529
+ """GUI compatibility: latest report, or None when absent/invalid/stale."""
530
+ return read_snapshot()[0]