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.
- package/.env.example +208 -0
- package/LICENSE +21 -0
- package/README.md +213 -2
- package/README.zh.md +214 -0
- package/carsi/LICENSE +21 -0
- package/carsi/NOTICE +5 -0
- package/carsi/README.md +75 -0
- package/carsi/paper_search_carsi/__init__.py +6 -0
- package/carsi/paper_search_carsi/_core/__init__.py +26 -0
- package/carsi/paper_search_carsi/_core/racing.pyx +280 -0
- package/carsi/paper_search_carsi/adapters/__init__.py +26 -0
- package/carsi/paper_search_carsi/adapters/acm/__init__.py +13 -0
- package/carsi/paper_search_carsi/adapters/acs/__init__.py +47 -0
- package/carsi/paper_search_carsi/adapters/aha_journals/__init__.py +13 -0
- package/carsi/paper_search_carsi/adapters/ams/__init__.py +19 -0
- package/carsi/paper_search_carsi/adapters/annual_reviews/__init__.py +154 -0
- package/carsi/paper_search_carsi/adapters/aps/__init__.py +127 -0
- package/carsi/paper_search_carsi/adapters/asce/__init__.py +13 -0
- package/carsi/paper_search_carsi/adapters/base.py +320 -0
- package/carsi/paper_search_carsi/adapters/bmj/__init__.py +32 -0
- package/carsi/paper_search_carsi/adapters/copernicus/__init__.py +20 -0
- package/carsi/paper_search_carsi/adapters/dovepress/__init__.py +43 -0
- package/carsi/paper_search_carsi/adapters/elsevier/__init__.py +302 -0
- package/carsi/paper_search_carsi/adapters/elsevier/journal_site.py +237 -0
- package/carsi/paper_search_carsi/adapters/elsevier/sciencedirect.py +138 -0
- package/carsi/paper_search_carsi/adapters/ieee/__init__.py +184 -0
- package/carsi/paper_search_carsi/adapters/iop/__init__.py +41 -0
- package/carsi/paper_search_carsi/adapters/jama/__init__.py +54 -0
- package/carsi/paper_search_carsi/adapters/karger/__init__.py +75 -0
- package/carsi/paper_search_carsi/adapters/lww/__init__.py +103 -0
- package/carsi/paper_search_carsi/adapters/lww/journals_lww.py +36 -0
- package/carsi/paper_search_carsi/adapters/lww/ovid.py +72 -0
- package/carsi/paper_search_carsi/adapters/mdpi/__init__.py +20 -0
- package/carsi/paper_search_carsi/adapters/nejm/__init__.py +18 -0
- package/carsi/paper_search_carsi/adapters/neurology/__init__.py +13 -0
- package/carsi/paper_search_carsi/adapters/optica/__init__.py +33 -0
- package/carsi/paper_search_carsi/adapters/oup/__init__.py +366 -0
- package/carsi/paper_search_carsi/adapters/platforms/__init__.py +5 -0
- package/carsi/paper_search_carsi/adapters/platforms/atypon/__init__.py +149 -0
- package/carsi/paper_search_carsi/adapters/platforms/silverchair/__init__.py +83 -0
- package/carsi/paper_search_carsi/adapters/plos/__init__.py +20 -0
- package/carsi/paper_search_carsi/adapters/pnas/__init__.py +13 -0
- package/carsi/paper_search_carsi/adapters/rsc/__init__.py +74 -0
- package/carsi/paper_search_carsi/adapters/sage/__init__.py +154 -0
- package/carsi/paper_search_carsi/adapters/science/__init__.py +13 -0
- package/carsi/paper_search_carsi/adapters/springer/__init__.py +62 -0
- package/carsi/paper_search_carsi/adapters/taylor_francis/__init__.py +46 -0
- package/carsi/paper_search_carsi/adapters/thieme/__init__.py +164 -0
- package/carsi/paper_search_carsi/adapters/wiley/__init__.py +145 -0
- package/carsi/paper_search_carsi/adapters/world_scientific/__init__.py +27 -0
- package/carsi/paper_search_carsi/assets/icon-32.png +0 -0
- package/carsi/paper_search_carsi/assets/icon-512.png +0 -0
- package/carsi/paper_search_carsi/assets/icon-64.png +0 -0
- package/carsi/paper_search_carsi/assets/icon.svg +43 -0
- package/carsi/paper_search_carsi/blocked_journals.py +63 -0
- package/carsi/paper_search_carsi/browser/__init__.py +1 -0
- package/carsi/paper_search_carsi/browser/backend.py +823 -0
- package/carsi/paper_search_carsi/browser/cookies.py +386 -0
- package/carsi/paper_search_carsi/browser/ego_relay.py +152 -0
- package/carsi/paper_search_carsi/browser/engine.py +927 -0
- package/carsi/paper_search_carsi/browser/login.py +336 -0
- package/carsi/paper_search_carsi/browser/preview.py +379 -0
- package/carsi/paper_search_carsi/browser/watchdog.py +193 -0
- package/carsi/paper_search_carsi/cache.py +62 -0
- package/carsi/paper_search_carsi/carsi_schools.py +109 -0
- package/carsi/paper_search_carsi/cli.py +479 -0
- package/carsi/paper_search_carsi/cold_start.py +251 -0
- package/carsi/paper_search_carsi/config.py +301 -0
- package/carsi/paper_search_carsi/data/institutional_identity_policy.json +165 -0
- package/carsi/paper_search_carsi/data/publisher_access_catalog.json +969 -0
- package/carsi/paper_search_carsi/data/publisher_browser_verification_matrix.json +625 -0
- package/carsi/paper_search_carsi/download_diagnostics.py +228 -0
- package/carsi/paper_search_carsi/engine/__init__.py +1 -0
- package/carsi/paper_search_carsi/engine/access_rules.py +345 -0
- package/carsi/paper_search_carsi/engine/batch.py +702 -0
- package/carsi/paper_search_carsi/engine/batch_access.py +483 -0
- package/carsi/paper_search_carsi/engine/batch_capture.py +1531 -0
- package/carsi/paper_search_carsi/engine/batch_common.py +427 -0
- package/carsi/paper_search_carsi/engine/batch_diagnostics.py +307 -0
- package/carsi/paper_search_carsi/engine/batch_institution.py +981 -0
- package/carsi/paper_search_carsi/engine/batch_login.py +1605 -0
- package/carsi/paper_search_carsi/engine/batch_pdf_urls.py +274 -0
- package/carsi/paper_search_carsi/engine/batch_runs.py +711 -0
- package/carsi/paper_search_carsi/engine/batch_user.py +173 -0
- package/carsi/paper_search_carsi/engine/batch_waits.py +441 -0
- package/carsi/paper_search_carsi/engine/bridge.py +666 -0
- package/carsi/paper_search_carsi/engine/pdf_guard.py +310 -0
- package/carsi/paper_search_carsi/engine/router.py +514 -0
- package/carsi/paper_search_carsi/engine/supplements.py +148 -0
- package/carsi/paper_search_carsi/entitlements.py +233 -0
- package/carsi/paper_search_carsi/extractors/__init__.py +0 -0
- package/carsi/paper_search_carsi/extractors/pdf_extractor.py +149 -0
- package/carsi/paper_search_carsi/identifiers.py +121 -0
- package/carsi/paper_search_carsi/issues.py +227 -0
- package/carsi/paper_search_carsi/log.py +84 -0
- package/carsi/paper_search_carsi/mcp_progress.py +82 -0
- package/carsi/paper_search_carsi/network.py +359 -0
- package/carsi/paper_search_carsi/orchestrator.py +130 -0
- package/carsi/paper_search_carsi/pdf_utils.py +279 -0
- package/carsi/paper_search_carsi/progress_bar.py +778 -0
- package/carsi/paper_search_carsi/progress_reporter.py +530 -0
- package/carsi/paper_search_carsi/progress_status.py +212 -0
- package/carsi/paper_search_carsi/publishers/__init__.py +10 -0
- package/carsi/paper_search_carsi/publishers/builtin/acm.json +65 -0
- package/carsi/paper_search_carsi/publishers/builtin/acs.json +73 -0
- package/carsi/paper_search_carsi/publishers/builtin/aha-journals.json +47 -0
- package/carsi/paper_search_carsi/publishers/builtin/aip.json +66 -0
- package/carsi/paper_search_carsi/publishers/builtin/ams.json +66 -0
- package/carsi/paper_search_carsi/publishers/builtin/annual-reviews.json +64 -0
- package/carsi/paper_search_carsi/publishers/builtin/aps.json +70 -0
- package/carsi/paper_search_carsi/publishers/builtin/asce.json +46 -0
- package/carsi/paper_search_carsi/publishers/builtin/bmj.json +46 -0
- package/carsi/paper_search_carsi/publishers/builtin/copernicus.json +45 -0
- package/carsi/paper_search_carsi/publishers/builtin/dovepress.json +35 -0
- package/carsi/paper_search_carsi/publishers/builtin/elsevier.json +77 -0
- package/carsi/paper_search_carsi/publishers/builtin/frontiers.json +62 -0
- package/carsi/paper_search_carsi/publishers/builtin/ieee.json +68 -0
- package/carsi/paper_search_carsi/publishers/builtin/iop.json +69 -0
- package/carsi/paper_search_carsi/publishers/builtin/jama.json +48 -0
- package/carsi/paper_search_carsi/publishers/builtin/karger.json +48 -0
- package/carsi/paper_search_carsi/publishers/builtin/lww.json +57 -0
- package/carsi/paper_search_carsi/publishers/builtin/mdpi.json +46 -0
- package/carsi/paper_search_carsi/publishers/builtin/nejm.json +46 -0
- package/carsi/paper_search_carsi/publishers/builtin/neurology.json +45 -0
- package/carsi/paper_search_carsi/publishers/builtin/optica.json +77 -0
- package/carsi/paper_search_carsi/publishers/builtin/oxfordacademic.json +64 -0
- package/carsi/paper_search_carsi/publishers/builtin/plos.json +46 -0
- package/carsi/paper_search_carsi/publishers/builtin/pnas.json +67 -0
- package/carsi/paper_search_carsi/publishers/builtin/royalsocietypublishing.json +62 -0
- package/carsi/paper_search_carsi/publishers/builtin/rsc.json +71 -0
- package/carsi/paper_search_carsi/publishers/builtin/sage.json +48 -0
- package/carsi/paper_search_carsi/publishers/builtin/science.json +68 -0
- package/carsi/paper_search_carsi/publishers/builtin/springer.json +92 -0
- package/carsi/paper_search_carsi/publishers/builtin/taylor-francis.json +52 -0
- package/carsi/paper_search_carsi/publishers/builtin/thieme.json +50 -0
- package/carsi/paper_search_carsi/publishers/builtin/wiley.json +77 -0
- package/carsi/paper_search_carsi/publishers/builtin/world-scientific.json +68 -0
- package/carsi/paper_search_carsi/publishers/extensions.py +241 -0
- package/carsi/paper_search_carsi/publishers/loader.py +93 -0
- package/carsi/paper_search_carsi/publishers/model.py +90 -0
- package/carsi/paper_search_carsi/publishers/profiles.py +127 -0
- package/carsi/paper_search_carsi/publishers/schema.json +129 -0
- package/carsi/paper_search_carsi/publishers/site.py +86 -0
- package/carsi/paper_search_carsi/rate_limit.py +51 -0
- package/carsi/paper_search_carsi/server.py +197 -0
- package/carsi/paper_search_carsi/session_broker.py +163 -0
- package/carsi/paper_search_carsi/tor.py +9 -0
- package/carsi/paper_search_carsi/user_settings.py +55 -0
- package/carsi/pyproject.toml +41 -0
- package/dist/capabilities/body-snippet-search/handler.d.ts +9 -0
- package/dist/capabilities/body-snippet-search/handler.d.ts.map +1 -0
- package/dist/capabilities/body-snippet-search/handler.js +17 -0
- package/dist/capabilities/body-snippet-search/handler.js.map +1 -0
- package/dist/capabilities/body-snippet-search/index.d.ts +7 -0
- package/dist/capabilities/body-snippet-search/index.d.ts.map +1 -0
- package/dist/capabilities/body-snippet-search/index.js +6 -0
- package/dist/capabilities/body-snippet-search/index.js.map +1 -0
- package/dist/capabilities/body-snippet-search/schemas.d.ts +36 -0
- package/dist/capabilities/body-snippet-search/schemas.d.ts.map +1 -0
- package/dist/capabilities/body-snippet-search/schemas.js +16 -0
- package/dist/capabilities/body-snippet-search/schemas.js.map +1 -0
- package/dist/capabilities/body-snippet-search/service.d.ts +4 -0
- package/dist/capabilities/body-snippet-search/service.d.ts.map +1 -0
- package/dist/capabilities/body-snippet-search/service.js +4 -0
- package/dist/capabilities/body-snippet-search/service.js.map +1 -0
- package/dist/capabilities/body-snippet-search/tools.d.ts +187 -0
- package/dist/capabilities/body-snippet-search/tools.d.ts.map +1 -0
- package/dist/capabilities/body-snippet-search/tools.js +49 -0
- package/dist/capabilities/body-snippet-search/tools.js.map +1 -0
- package/dist/capabilities/body-snippet-search/types.d.ts +14 -0
- package/dist/capabilities/body-snippet-search/types.d.ts.map +1 -0
- package/dist/capabilities/body-snippet-search/types.js +2 -0
- package/dist/capabilities/body-snippet-search/types.js.map +1 -0
- package/dist/capabilities/citation-expansion/CitationService.d.ts +47 -0
- package/dist/capabilities/citation-expansion/CitationService.d.ts.map +1 -0
- package/dist/capabilities/citation-expansion/CitationService.js +243 -0
- package/dist/capabilities/citation-expansion/CitationService.js.map +1 -0
- package/dist/capabilities/citation-expansion/handler.d.ts +36 -0
- package/dist/capabilities/citation-expansion/handler.d.ts.map +1 -0
- package/dist/capabilities/citation-expansion/handler.js +44 -0
- package/dist/capabilities/citation-expansion/handler.js.map +1 -0
- package/dist/capabilities/citation-expansion/index.d.ts +6 -0
- package/dist/capabilities/citation-expansion/index.d.ts.map +1 -0
- package/dist/capabilities/citation-expansion/index.js +5 -0
- package/dist/capabilities/citation-expansion/index.js.map +1 -0
- package/dist/capabilities/citation-expansion/schemas.d.ts +28 -0
- package/dist/capabilities/citation-expansion/schemas.d.ts.map +1 -0
- package/dist/capabilities/citation-expansion/schemas.js +13 -0
- package/dist/capabilities/citation-expansion/schemas.js.map +1 -0
- package/dist/capabilities/citation-expansion/tools.d.ts +82 -0
- package/dist/capabilities/citation-expansion/tools.d.ts.map +1 -0
- package/dist/capabilities/citation-expansion/tools.js +48 -0
- package/dist/capabilities/citation-expansion/tools.js.map +1 -0
- package/dist/capabilities/citation-expansion/types.d.ts +22 -0
- package/dist/capabilities/citation-expansion/types.d.ts.map +1 -0
- package/dist/capabilities/citation-expansion/types.js +2 -0
- package/dist/capabilities/citation-expansion/types.js.map +1 -0
- package/dist/capabilities/journal-metrics/JournalMetricsService.d.ts +3 -0
- package/dist/capabilities/journal-metrics/JournalMetricsService.d.ts.map +1 -0
- package/dist/capabilities/journal-metrics/JournalMetricsService.js +142 -0
- package/dist/capabilities/journal-metrics/JournalMetricsService.js.map +1 -0
- package/dist/capabilities/journal-metrics/handler.d.ts +12 -0
- package/dist/capabilities/journal-metrics/handler.d.ts.map +1 -0
- package/dist/capabilities/journal-metrics/handler.js +38 -0
- package/dist/capabilities/journal-metrics/handler.js.map +1 -0
- package/dist/capabilities/journal-metrics/index.d.ts +6 -0
- package/dist/capabilities/journal-metrics/index.d.ts.map +1 -0
- package/dist/capabilities/journal-metrics/index.js +5 -0
- package/dist/capabilities/journal-metrics/index.js.map +1 -0
- package/dist/capabilities/journal-metrics/schemas.d.ts +28 -0
- package/dist/capabilities/journal-metrics/schemas.d.ts.map +1 -0
- package/dist/capabilities/journal-metrics/schemas.js +13 -0
- package/dist/capabilities/journal-metrics/schemas.js.map +1 -0
- package/dist/capabilities/journal-metrics/tools.d.ts +67 -0
- package/dist/capabilities/journal-metrics/tools.d.ts.map +1 -0
- package/dist/capabilities/journal-metrics/tools.js +27 -0
- package/dist/capabilities/journal-metrics/tools.js.map +1 -0
- package/dist/capabilities/journal-metrics/types.d.ts +36 -0
- package/dist/capabilities/journal-metrics/types.d.ts.map +1 -0
- package/dist/capabilities/journal-metrics/types.js +2 -0
- package/dist/capabilities/journal-metrics/types.js.map +1 -0
- package/dist/capabilities/metadata-search/MultiSourceSearchService.d.ts +9 -0
- package/dist/capabilities/metadata-search/MultiSourceSearchService.d.ts.map +1 -0
- package/dist/capabilities/metadata-search/MultiSourceSearchService.js +81 -0
- package/dist/capabilities/metadata-search/MultiSourceSearchService.js.map +1 -0
- package/dist/capabilities/metadata-search/handler.d.ts +23 -0
- package/dist/capabilities/metadata-search/handler.d.ts.map +1 -0
- package/dist/capabilities/metadata-search/handler.js +153 -0
- package/dist/capabilities/metadata-search/handler.js.map +1 -0
- package/dist/capabilities/metadata-search/index.d.ts +6 -0
- package/dist/capabilities/metadata-search/index.d.ts.map +1 -0
- package/dist/capabilities/metadata-search/index.js +5 -0
- package/dist/capabilities/metadata-search/index.js.map +1 -0
- package/dist/capabilities/metadata-search/schemas.d.ts +83 -0
- package/dist/capabilities/metadata-search/schemas.d.ts.map +1 -0
- package/dist/capabilities/metadata-search/schemas.js +80 -0
- package/dist/capabilities/metadata-search/schemas.js.map +1 -0
- package/dist/capabilities/metadata-search/tools.d.ts +178 -0
- package/dist/capabilities/metadata-search/tools.d.ts.map +1 -0
- package/dist/capabilities/metadata-search/tools.js +75 -0
- package/dist/capabilities/metadata-search/tools.js.map +1 -0
- package/dist/capabilities/metadata-search/types.d.ts +13 -0
- package/dist/capabilities/metadata-search/types.d.ts.map +1 -0
- package/dist/capabilities/metadata-search/types.js +2 -0
- package/dist/capabilities/metadata-search/types.js.map +1 -0
- package/dist/capabilities/pdf-discovery/DownloadTier.d.ts +43 -0
- package/dist/capabilities/pdf-discovery/DownloadTier.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/DownloadTier.js +2 -0
- package/dist/capabilities/pdf-discovery/DownloadTier.js.map +1 -0
- package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.d.ts +16 -0
- package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js +202 -0
- package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js.map +1 -0
- package/dist/capabilities/pdf-discovery/batch.d.ts +74 -0
- package/dist/capabilities/pdf-discovery/batch.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/batch.js +370 -0
- package/dist/capabilities/pdf-discovery/batch.js.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.d.ts +7 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.js +46 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiAttempt.js.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.d.ts +35 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.js +689 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiMcpClient.js.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.d.ts +23 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.js +30 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiProgress.js.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.d.ts +29 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.js +176 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiReadiness.js.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.d.ts +8 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.js +225 -0
- package/dist/capabilities/pdf-discovery/carsi/CarsiResultNormalizer.js.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/index.d.ts +8 -0
- package/dist/capabilities/pdf-discovery/carsi/index.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/index.js +11 -0
- package/dist/capabilities/pdf-discovery/carsi/index.js.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/types.d.ts +118 -0
- package/dist/capabilities/pdf-discovery/carsi/types.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/carsi/types.js +2 -0
- package/dist/capabilities/pdf-discovery/carsi/types.js.map +1 -0
- package/dist/capabilities/pdf-discovery/handler.d.ts +28 -0
- package/dist/capabilities/pdf-discovery/handler.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/handler.js +72 -0
- package/dist/capabilities/pdf-discovery/handler.js.map +1 -0
- package/dist/capabilities/pdf-discovery/identity.d.ts +8 -0
- package/dist/capabilities/pdf-discovery/identity.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/identity.js +24 -0
- package/dist/capabilities/pdf-discovery/identity.js.map +1 -0
- package/dist/capabilities/pdf-discovery/index.d.ts +8 -0
- package/dist/capabilities/pdf-discovery/index.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/index.js +6 -0
- package/dist/capabilities/pdf-discovery/index.js.map +1 -0
- package/dist/capabilities/pdf-discovery/oaHosts.d.ts +3 -0
- package/dist/capabilities/pdf-discovery/oaHosts.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/oaHosts.js +37 -0
- package/dist/capabilities/pdf-discovery/oaHosts.js.map +1 -0
- package/dist/capabilities/pdf-discovery/pmcCloud.d.ts +6 -0
- package/dist/capabilities/pdf-discovery/pmcCloud.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/pmcCloud.js +95 -0
- package/dist/capabilities/pdf-discovery/pmcCloud.js.map +1 -0
- package/dist/capabilities/pdf-discovery/publisherApi/elsevier.d.ts +4 -0
- package/dist/capabilities/pdf-discovery/publisherApi/elsevier.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/publisherApi/elsevier.js +122 -0
- package/dist/capabilities/pdf-discovery/publisherApi/elsevier.js.map +1 -0
- package/dist/capabilities/pdf-discovery/publisherApi/index.d.ts +31 -0
- package/dist/capabilities/pdf-discovery/publisherApi/index.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/publisherApi/index.js +20 -0
- package/dist/capabilities/pdf-discovery/publisherApi/index.js.map +1 -0
- package/dist/capabilities/pdf-discovery/publisherApi/springer.d.ts +4 -0
- package/dist/capabilities/pdf-discovery/publisherApi/springer.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/publisherApi/springer.js +114 -0
- package/dist/capabilities/pdf-discovery/publisherApi/springer.js.map +1 -0
- package/dist/capabilities/pdf-discovery/schemas.d.ts +176 -0
- package/dist/capabilities/pdf-discovery/schemas.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/schemas.js +72 -0
- package/dist/capabilities/pdf-discovery/schemas.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.d.ts +5 -0
- package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js +40 -0
- package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/entitledAccess.d.ts +13 -0
- package/dist/capabilities/pdf-discovery/tiers/entitledAccess.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/entitledAccess.js +123 -0
- package/dist/capabilities/pdf-discovery/tiers/entitledAccess.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/primary.d.ts +3 -0
- package/dist/capabilities/pdf-discovery/tiers/primary.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/primary.js +21 -0
- package/dist/capabilities/pdf-discovery/tiers/primary.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/publisherApi.d.ts +12 -0
- package/dist/capabilities/pdf-discovery/tiers/publisherApi.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/publisherApi.js +109 -0
- package/dist/capabilities/pdf-discovery/tiers/publisherApi.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/repositories.d.ts +10 -0
- package/dist/capabilities/pdf-discovery/tiers/repositories.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/repositories.js +37 -0
- package/dist/capabilities/pdf-discovery/tiers/repositories.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/scihub.d.ts +3 -0
- package/dist/capabilities/pdf-discovery/tiers/scihub.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/scihub.js +21 -0
- package/dist/capabilities/pdf-discovery/tiers/scihub.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/unpaywall.d.ts +3 -0
- package/dist/capabilities/pdf-discovery/tiers/unpaywall.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tiers/unpaywall.js +76 -0
- package/dist/capabilities/pdf-discovery/tiers/unpaywall.js.map +1 -0
- package/dist/capabilities/pdf-discovery/tools.d.ts +337 -0
- package/dist/capabilities/pdf-discovery/tools.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/tools.js +144 -0
- package/dist/capabilities/pdf-discovery/tools.js.map +1 -0
- package/dist/capabilities/pdf-discovery/types.d.ts +86 -0
- package/dist/capabilities/pdf-discovery/types.d.ts.map +1 -0
- package/dist/capabilities/pdf-discovery/types.js +2 -0
- package/dist/capabilities/pdf-discovery/types.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1196 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/ConfigService.d.ts +28 -0
- package/dist/config/ConfigService.d.ts.map +1 -0
- package/dist/config/ConfigService.js +204 -0
- package/dist/config/ConfigService.js.map +1 -0
- package/dist/config/ResultCaps.d.ts +4 -0
- package/dist/config/ResultCaps.d.ts.map +1 -0
- package/dist/config/ResultCaps.js +10 -0
- package/dist/config/ResultCaps.js.map +1 -0
- package/dist/config/constants.d.ts +158 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +102 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/core/capabilityProfile.d.ts +2 -0
- package/dist/core/capabilityProfile.d.ts.map +1 -0
- package/dist/core/capabilityProfile.js +2 -0
- package/dist/core/capabilityProfile.js.map +1 -0
- package/dist/core/diagnostics.d.ts +2 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/handleToolCall.d.ts +13 -0
- package/dist/core/handleToolCall.d.ts.map +1 -0
- package/dist/core/handleToolCall.js +304 -0
- package/dist/core/handleToolCall.js.map +1 -0
- package/dist/core/httpPolicies.d.ts +2 -0
- package/dist/core/httpPolicies.d.ts.map +1 -0
- package/dist/core/httpPolicies.js +2 -0
- package/dist/core/httpPolicies.js.map +1 -0
- package/dist/core/liveSmoke.d.ts +2 -0
- package/dist/core/liveSmoke.d.ts.map +1 -0
- package/dist/core/liveSmoke.js +2 -0
- package/dist/core/liveSmoke.js.map +1 -0
- package/dist/core/platformFactories.d.ts +2 -0
- package/dist/core/platformFactories.d.ts.map +1 -0
- package/dist/core/platformFactories.js +2 -0
- package/dist/core/platformFactories.js.map +1 -0
- package/dist/core/platformMetadata.d.ts +2 -0
- package/dist/core/platformMetadata.d.ts.map +1 -0
- package/dist/core/platformMetadata.js +2 -0
- package/dist/core/platformMetadata.js.map +1 -0
- package/dist/core/schemas.d.ts +365 -0
- package/dist/core/schemas.d.ts.map +1 -0
- package/dist/core/schemas.js +256 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/searchers.d.ts +41 -0
- package/dist/core/searchers.d.ts.map +1 -0
- package/dist/core/searchers.js +24 -0
- package/dist/core/searchers.js.map +1 -0
- package/dist/core/textReports.d.ts +2 -0
- package/dist/core/textReports.d.ts.map +1 -0
- package/dist/core/textReports.js +2 -0
- package/dist/core/textReports.js.map +1 -0
- package/dist/core/tools.d.ts +7 -0
- package/dist/core/tools.d.ts.map +1 -0
- package/dist/core/tools.js +489 -0
- package/dist/core/tools.js.map +1 -0
- package/dist/infrastructure/cache/RequestCache.d.ts +26 -0
- package/dist/infrastructure/cache/RequestCache.d.ts.map +1 -0
- package/dist/infrastructure/cache/RequestCache.js +66 -0
- package/dist/infrastructure/cache/RequestCache.js.map +1 -0
- package/dist/infrastructure/http/ErrorHandler.d.ts +101 -0
- package/dist/infrastructure/http/ErrorHandler.d.ts.map +1 -0
- package/dist/infrastructure/http/ErrorHandler.js +278 -0
- package/dist/infrastructure/http/ErrorHandler.js.map +1 -0
- package/dist/infrastructure/http/HttpClient.d.ts +31 -0
- package/dist/infrastructure/http/HttpClient.d.ts.map +1 -0
- package/dist/infrastructure/http/HttpClient.js +52 -0
- package/dist/infrastructure/http/HttpClient.js.map +1 -0
- package/dist/infrastructure/pdf/PDFExtractor.d.ts +34 -0
- package/dist/infrastructure/pdf/PDFExtractor.d.ts.map +1 -0
- package/dist/infrastructure/pdf/PDFExtractor.js +130 -0
- package/dist/infrastructure/pdf/PDFExtractor.js.map +1 -0
- package/dist/infrastructure/pdf/PdfDownload.d.ts +16 -0
- package/dist/infrastructure/pdf/PdfDownload.d.ts.map +1 -0
- package/dist/infrastructure/pdf/PdfDownload.js +242 -0
- package/dist/infrastructure/pdf/PdfDownload.js.map +1 -0
- package/dist/infrastructure/pdf/PdfFirstPage.cjs +70 -0
- package/dist/infrastructure/pdf/PdfFirstPage.cjs.map +1 -0
- package/dist/infrastructure/pdf/PdfFirstPage.d.cts +16 -0
- package/dist/infrastructure/pdf/PdfFirstPage.d.cts.map +1 -0
- package/dist/infrastructure/pdf/PdfIdentity.d.ts +29 -0
- package/dist/infrastructure/pdf/PdfIdentity.d.ts.map +1 -0
- package/dist/infrastructure/pdf/PdfIdentity.js +283 -0
- package/dist/infrastructure/pdf/PdfIdentity.js.map +1 -0
- package/dist/infrastructure/rate-limit/RateLimiter.d.ts +50 -0
- package/dist/infrastructure/rate-limit/RateLimiter.d.ts.map +1 -0
- package/dist/infrastructure/rate-limit/RateLimiter.js +128 -0
- package/dist/infrastructure/rate-limit/RateLimiter.js.map +1 -0
- package/dist/infrastructure/runlog/RunLedger.d.ts +61 -0
- package/dist/infrastructure/runlog/RunLedger.d.ts.map +1 -0
- package/dist/infrastructure/runlog/RunLedger.js +284 -0
- package/dist/infrastructure/runlog/RunLedger.js.map +1 -0
- package/dist/infrastructure/runlog/failureClass.d.ts +26 -0
- package/dist/infrastructure/runlog/failureClass.d.ts.map +1 -0
- package/dist/infrastructure/runlog/failureClass.js +125 -0
- package/dist/infrastructure/runlog/failureClass.js.map +1 -0
- package/dist/infrastructure/security/SecurityUtils.d.ts +80 -0
- package/dist/infrastructure/security/SecurityUtils.d.ts.map +1 -0
- package/dist/infrastructure/security/SecurityUtils.js +357 -0
- package/dist/infrastructure/security/SecurityUtils.js.map +1 -0
- package/dist/management/capability-profile/carsiReadiness.d.ts +10 -0
- package/dist/management/capability-profile/carsiReadiness.d.ts.map +1 -0
- package/dist/management/capability-profile/carsiReadiness.js +13 -0
- package/dist/management/capability-profile/carsiReadiness.js.map +1 -0
- package/dist/management/capability-profile/index.d.ts +26 -0
- package/dist/management/capability-profile/index.d.ts.map +1 -0
- package/dist/management/capability-profile/index.js +183 -0
- package/dist/management/capability-profile/index.js.map +1 -0
- package/dist/management/carsi/host.d.ts +4 -0
- package/dist/management/carsi/host.d.ts.map +1 -0
- package/dist/management/carsi/host.js +65 -0
- package/dist/management/carsi/host.js.map +1 -0
- package/dist/management/carsi/installer.d.ts +146 -0
- package/dist/management/carsi/installer.d.ts.map +1 -0
- package/dist/management/carsi/installer.js +527 -0
- package/dist/management/carsi/installer.js.map +1 -0
- package/dist/management/carsi/runtime.d.ts +31 -0
- package/dist/management/carsi/runtime.d.ts.map +1 -0
- package/dist/management/carsi/runtime.js +67 -0
- package/dist/management/carsi/runtime.js.map +1 -0
- package/dist/management/config/index.d.ts +2 -0
- package/dist/management/config/index.d.ts.map +1 -0
- package/dist/management/config/index.js +2 -0
- package/dist/management/config/index.js.map +1 -0
- package/dist/management/diagnostics/index.d.ts +43 -0
- package/dist/management/diagnostics/index.d.ts.map +1 -0
- package/dist/management/diagnostics/index.js +610 -0
- package/dist/management/diagnostics/index.js.map +1 -0
- package/dist/management/failures/index.d.ts +47 -0
- package/dist/management/failures/index.d.ts.map +1 -0
- package/dist/management/failures/index.js +134 -0
- package/dist/management/failures/index.js.map +1 -0
- package/dist/management/reports/index.d.ts +32 -0
- package/dist/management/reports/index.d.ts.map +1 -0
- package/dist/management/reports/index.js +123 -0
- package/dist/management/reports/index.js.map +1 -0
- package/dist/management/skills/autoSync.d.ts +41 -0
- package/dist/management/skills/autoSync.d.ts.map +1 -0
- package/dist/management/skills/autoSync.js +142 -0
- package/dist/management/skills/autoSync.js.map +1 -0
- package/dist/management/skills/index.d.ts +3 -0
- package/dist/management/skills/index.d.ts.map +1 -0
- package/dist/management/skills/index.js +3 -0
- package/dist/management/skills/index.js.map +1 -0
- package/dist/management/smoke/index.d.ts +44 -0
- package/dist/management/smoke/index.d.ts.map +1 -0
- package/dist/management/smoke/index.js +254 -0
- package/dist/management/smoke/index.js.map +1 -0
- package/dist/models/Paper.d.ts +64 -0
- package/dist/models/Paper.d.ts.map +1 -0
- package/dist/models/Paper.js +70 -0
- package/dist/models/Paper.js.map +1 -0
- package/dist/platforms/ACMSearcher.d.ts +16 -0
- package/dist/platforms/ACMSearcher.d.ts.map +1 -0
- package/dist/platforms/ACMSearcher.js +125 -0
- package/dist/platforms/ACMSearcher.js.map +1 -0
- package/dist/platforms/BioRxivSearcher.d.ts +47 -0
- package/dist/platforms/BioRxivSearcher.d.ts.map +1 -0
- package/dist/platforms/BioRxivSearcher.js +215 -0
- package/dist/platforms/BioRxivSearcher.js.map +1 -0
- package/dist/platforms/CORESearcher.d.ts +17 -0
- package/dist/platforms/CORESearcher.d.ts.map +1 -0
- package/dist/platforms/CORESearcher.js +185 -0
- package/dist/platforms/CORESearcher.js.map +1 -0
- package/dist/platforms/CrossrefSearcher.d.ts +34 -0
- package/dist/platforms/CrossrefSearcher.d.ts.map +1 -0
- package/dist/platforms/CrossrefSearcher.js +339 -0
- package/dist/platforms/CrossrefSearcher.js.map +1 -0
- package/dist/platforms/DBLPSearcher.d.ts +16 -0
- package/dist/platforms/DBLPSearcher.d.ts.map +1 -0
- package/dist/platforms/DBLPSearcher.js +116 -0
- package/dist/platforms/DBLPSearcher.js.map +1 -0
- package/dist/platforms/EuropePMCSearcher.d.ts +20 -0
- package/dist/platforms/EuropePMCSearcher.d.ts.map +1 -0
- package/dist/platforms/EuropePMCSearcher.js +173 -0
- package/dist/platforms/EuropePMCSearcher.js.map +1 -0
- package/dist/platforms/GoogleScholarSearcher.d.ts +142 -0
- package/dist/platforms/GoogleScholarSearcher.d.ts.map +1 -0
- package/dist/platforms/GoogleScholarSearcher.js +648 -0
- package/dist/platforms/GoogleScholarSearcher.js.map +1 -0
- package/dist/platforms/IACRSearcher.d.ts +51 -0
- package/dist/platforms/IACRSearcher.d.ts.map +1 -0
- package/dist/platforms/IACRSearcher.js +339 -0
- package/dist/platforms/IACRSearcher.js.map +1 -0
- package/dist/platforms/IEEESearcher.d.ts +15 -0
- package/dist/platforms/IEEESearcher.d.ts.map +1 -0
- package/dist/platforms/IEEESearcher.js +123 -0
- package/dist/platforms/IEEESearcher.js.map +1 -0
- package/dist/platforms/OpenAIRESearcher.d.ts +22 -0
- package/dist/platforms/OpenAIRESearcher.d.ts.map +1 -0
- package/dist/platforms/OpenAIRESearcher.js +235 -0
- package/dist/platforms/OpenAIRESearcher.js.map +1 -0
- package/dist/platforms/OpenAlexSearcher.d.ts +14 -0
- package/dist/platforms/OpenAlexSearcher.d.ts.map +1 -0
- package/dist/platforms/OpenAlexSearcher.js +114 -0
- package/dist/platforms/OpenAlexSearcher.js.map +1 -0
- package/dist/platforms/OpenReviewSearcher.d.ts +19 -0
- package/dist/platforms/OpenReviewSearcher.d.ts.map +1 -0
- package/dist/platforms/OpenReviewSearcher.js +141 -0
- package/dist/platforms/OpenReviewSearcher.js.map +1 -0
- package/dist/platforms/PMCSearcher.d.ts +20 -0
- package/dist/platforms/PMCSearcher.d.ts.map +1 -0
- package/dist/platforms/PMCSearcher.js +177 -0
- package/dist/platforms/PMCSearcher.js.map +1 -0
- package/dist/platforms/PaperSource.d.ts +149 -0
- package/dist/platforms/PaperSource.d.ts.map +1 -0
- package/dist/platforms/PaperSource.js +125 -0
- package/dist/platforms/PaperSource.js.map +1 -0
- package/dist/platforms/PubMedSearcher.d.ts +104 -0
- package/dist/platforms/PubMedSearcher.d.ts.map +1 -0
- package/dist/platforms/PubMedSearcher.js +430 -0
- package/dist/platforms/PubMedSearcher.js.map +1 -0
- package/dist/platforms/SciHubSearcher.d.ts +66 -0
- package/dist/platforms/SciHubSearcher.d.ts.map +1 -0
- package/dist/platforms/SciHubSearcher.js +398 -0
- package/dist/platforms/SciHubSearcher.js.map +1 -0
- package/dist/platforms/ScienceDirectSearcher.d.ts +42 -0
- package/dist/platforms/ScienceDirectSearcher.d.ts.map +1 -0
- package/dist/platforms/ScienceDirectSearcher.js +334 -0
- package/dist/platforms/ScienceDirectSearcher.js.map +1 -0
- package/dist/platforms/ScopusSearcher.d.ts +43 -0
- package/dist/platforms/ScopusSearcher.d.ts.map +1 -0
- package/dist/platforms/ScopusSearcher.js +377 -0
- package/dist/platforms/ScopusSearcher.js.map +1 -0
- package/dist/platforms/SemanticScholarSearcher.d.ts +96 -0
- package/dist/platforms/SemanticScholarSearcher.d.ts.map +1 -0
- package/dist/platforms/SemanticScholarSearcher.js +419 -0
- package/dist/platforms/SemanticScholarSearcher.js.map +1 -0
- package/dist/platforms/SpringerSearcher.d.ts +54 -0
- package/dist/platforms/SpringerSearcher.d.ts.map +1 -0
- package/dist/platforms/SpringerSearcher.js +387 -0
- package/dist/platforms/SpringerSearcher.js.map +1 -0
- package/dist/platforms/USENIXSearcher.d.ts +14 -0
- package/dist/platforms/USENIXSearcher.d.ts.map +1 -0
- package/dist/platforms/USENIXSearcher.js +75 -0
- package/dist/platforms/USENIXSearcher.js.map +1 -0
- package/dist/platforms/UnpaywallSearcher.d.ts +29 -0
- package/dist/platforms/UnpaywallSearcher.d.ts.map +1 -0
- package/dist/platforms/UnpaywallSearcher.js +135 -0
- package/dist/platforms/UnpaywallSearcher.js.map +1 -0
- package/dist/platforms/WebOfScienceSearcher.d.ts +111 -0
- package/dist/platforms/WebOfScienceSearcher.d.ts.map +1 -0
- package/dist/platforms/WebOfScienceSearcher.js +500 -0
- package/dist/platforms/WebOfScienceSearcher.js.map +1 -0
- package/dist/registry/aliases.d.ts +2 -0
- package/dist/registry/aliases.d.ts.map +1 -0
- package/dist/registry/aliases.js +2 -0
- package/dist/registry/aliases.js.map +1 -0
- package/dist/registry/httpPolicies.d.ts +3 -0
- package/dist/registry/httpPolicies.d.ts.map +1 -0
- package/dist/registry/httpPolicies.js +13 -0
- package/dist/registry/httpPolicies.js.map +1 -0
- package/dist/registry/platformFactories.d.ts +8 -0
- package/dist/registry/platformFactories.d.ts.map +1 -0
- package/dist/registry/platformFactories.js +51 -0
- package/dist/registry/platformFactories.js.map +1 -0
- package/dist/registry/platformMetadata.d.ts +55 -0
- package/dist/registry/platformMetadata.d.ts.map +1 -0
- package/dist/registry/platformMetadata.js +438 -0
- package/dist/registry/platformMetadata.js.map +1 -0
- package/dist/services/CitationService.d.ts +3 -0
- package/dist/services/CitationService.d.ts.map +1 -0
- package/dist/services/CitationService.js +2 -0
- package/dist/services/CitationService.js.map +1 -0
- package/dist/services/JournalMetricsService.d.ts +3 -0
- package/dist/services/JournalMetricsService.d.ts.map +1 -0
- package/dist/services/JournalMetricsService.js +2 -0
- package/dist/services/JournalMetricsService.js.map +1 -0
- package/dist/services/MultiSourceSearchService.d.ts +3 -0
- package/dist/services/MultiSourceSearchService.d.ts.map +1 -0
- package/dist/services/MultiSourceSearchService.js +2 -0
- package/dist/services/MultiSourceSearchService.js.map +1 -0
- package/dist/services/OpenAccessFallbackService.d.ts +4 -0
- package/dist/services/OpenAccessFallbackService.d.ts.map +1 -0
- package/dist/services/OpenAccessFallbackService.js +2 -0
- package/dist/services/OpenAccessFallbackService.js.map +1 -0
- package/dist/skills/SkillInstaller.d.ts +110 -0
- package/dist/skills/SkillInstaller.d.ts.map +1 -0
- package/dist/skills/SkillInstaller.js +393 -0
- package/dist/skills/SkillInstaller.js.map +1 -0
- package/dist/utils/ErrorHandler.d.ts +3 -0
- package/dist/utils/ErrorHandler.d.ts.map +1 -0
- package/dist/utils/ErrorHandler.js +3 -0
- package/dist/utils/ErrorHandler.js.map +1 -0
- package/dist/utils/HttpClient.d.ts +2 -0
- package/dist/utils/HttpClient.d.ts.map +1 -0
- package/dist/utils/HttpClient.js +2 -0
- package/dist/utils/HttpClient.js.map +1 -0
- package/dist/utils/Logger.d.ts +6 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +26 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/PDFExtractor.d.ts +3 -0
- package/dist/utils/PDFExtractor.d.ts.map +1 -0
- package/dist/utils/PDFExtractor.js +3 -0
- package/dist/utils/PDFExtractor.js.map +1 -0
- package/dist/utils/PdfDownload.d.ts +2 -0
- package/dist/utils/PdfDownload.d.ts.map +1 -0
- package/dist/utils/PdfDownload.js +2 -0
- package/dist/utils/PdfDownload.js.map +1 -0
- package/dist/utils/QuotaManager.d.ts +32 -0
- package/dist/utils/QuotaManager.d.ts.map +1 -0
- package/dist/utils/QuotaManager.js +95 -0
- package/dist/utils/QuotaManager.js.map +1 -0
- package/dist/utils/RateLimiter.d.ts +2 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +2 -0
- package/dist/utils/RateLimiter.js.map +1 -0
- package/dist/utils/RequestCache.d.ts +3 -0
- package/dist/utils/RequestCache.d.ts.map +1 -0
- package/dist/utils/RequestCache.js +3 -0
- package/dist/utils/RequestCache.js.map +1 -0
- package/dist/utils/SecurityUtils.d.ts +3 -0
- package/dist/utils/SecurityUtils.d.ts.map +1 -0
- package/dist/utils/SecurityUtils.js +3 -0
- package/dist/utils/SecurityUtils.js.map +1 -0
- package/package.json +118 -4
- package/skills/paper-search/SKILL.md +151 -0
- package/skills/paper-search/references/browser-download.md +101 -0
- package/skills/paper-search/references/capability-routing.md +174 -0
- package/skills/paper-search/references/carsi-setup.md +69 -0
- package/skills/paper-search/references/cli-contract.md +200 -0
- package/skills/paper-search/references/management-layer.md +149 -0
- package/skills/paper-search/references/publisher-support.md +72 -0
- 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]
|