dsh-paper-search 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,107 @@
1
+ # Changelog
2
+
3
+ ## Compatibility
4
+
5
+ | dsh-paper-search | DeepSeek Harness | Diuji pada |
6
+ |---|---|---|
7
+ | 0.1.0 | 0.1.7-rc.2 | 28 Sep 2026, Windows, Node 24 |
8
+
9
+ Paket ini tidak memakai API internal DSH selain `ctx.tools.register` dan
10
+ `ctx.get('skills').registerProvider` — keduanya sudah dipakai plugin lain yang
11
+ berjalan di profil ini (`dsh-search-cascade`), jadi titik patahnya sama dan sudah
12
+ diketahui.
13
+
14
+ ## [0.1.0] - 2026-09-28
15
+
16
+ Rilis pertama.
17
+
18
+ ### Added
19
+
20
+ - **Enam belas sumber lewat satu tool `paper_search`**, semuanya native HTTP:
21
+ - **tiga belas internasional** — Crossref, OpenAlex, DOAJ, Europe PMC, PubMed,
22
+ PubMed Central, arXiv, Semantic Scholar, CORE, Zenodo, HAL lewat REST API
23
+ JSON; IACR ePrint lewat penguraian HTML; Unpaywall lewat REST API dengan
24
+ **input DOI** (bukan kata kunci);
25
+ - **tiga portal Indonesia** lewat penguraian halaman publik — Garuda (artikel
26
+ jurnal), Sinta (daftar jurnal terakreditasi), IOS OneSearch (agregator
27
+ repositori).
28
+ - **Sumber ber-input DOI ditandai di registry** (`input: 'doi'`). Saat pengguna
29
+ mencari dengan kata kunci, `unpaywall` **dilewati** — bukan dijalankan lalu
30
+ dilaporkan kosong — dan alasan pelewatan itu dicetak dalam keluaran. Saat
31
+ diminta eksplisit, ia tetap dijalankan dan mengembalikan kosong, karena
32
+ memang begitulah perilakunya.
33
+ - **Dua sumber wajib kredensial**, masing-masing dilaporkan sebagai kegagalan
34
+ yang bisa ditindaklanjuti, bukan 429 yang membingungkan:
35
+ `core` butuh `CORE_API_KEY`; `unpaywall` butuh email.
36
+ - **Dedup** berdasarkan DOI lalu judul yang dinormalkan; setiap hasil mencatat
37
+ **semua** sumber yang menemukannya, bukan menyembunyikan yang lain.
38
+ - **Tool `paper_status`** — mengirim satu permintaan kecil ke tiap sumber dan
39
+ melaporkan mana yang benar-benar menjawab, plus kredensial mana yang terbaca
40
+ **beserta dari mana asalnya** (config plugin / variabel lingkungan /
41
+ `.env` milik CLI upstream).
42
+ - **Registrasi skill `paper-search`** lewat `ctx.get('skills').registerProvider()`
43
+ di rank **450** — sengaja kalah dari berkas di `~/.dsh/skills` (rank 400)
44
+ supaya suntingan pengguna selalu menang. Dibaca ulang setiap kali, tidak
45
+ di-cache, jadi suntingan `SKILL.md` langsung terpakai tanpa restart.
46
+ - **Pembatas laju arXiv sendiri** (1 permintaan / 3 detik) sesuai TOU mereka,
47
+ walau sumber lain dipanggil paralel.
48
+
49
+ ### Fixed
50
+
51
+ - Judul Garuda yang diulang oleh markup `<xmp>`-nya (terukur: judul 44 huruf
52
+ diulang ~6× menjadi 255 huruf) kini diringkas menjadi satu salinan. Aturannya
53
+ konservatif — hanya bila pengulangannya persis dari awal sampai akhir.
54
+ - DOI dinormalkan tanpa awalan URL (`https://doi.org/...`) supaya dedup bekerja
55
+ lintas sumber.
56
+
57
+ ### Catatan kejujuran
58
+
59
+ - **Sembilan sumber publik SENGAJA TIDAK didaftarkan**, masing-masing dengan
60
+ alasan yang diukur 28 Sep 2026 — bukan karena lupa:
61
+
62
+ | Sumber | Alasan |
63
+ |---|---|
64
+ | `moraref` | hanya halaman pendarat, tanpa markup hasil (3.964 huruf) |
65
+ | `dblp` | API di balik proteksi bot — mengembalikan halaman "Making sure you're not a bot!", bukan JSON |
66
+ | `openaire` | permintaan uji **timeout 30 detik dan 40 detik** |
67
+ | `biorxiv` | API-nya berbasis **rentang tanggal**, bukan pencarian kata kunci |
68
+ | `medrxiv` | sama seperti `biorxiv` |
69
+ | `base` | OAuth/OAI-PMH menolak: `Access denied for IP address 182.8.249.89…` — butuh pendaftaran IP institusi |
70
+ | `citeseerx` | HTTP 404, lalu **menggantung** sampai permintaannya dibunuh |
71
+ | `ssrn` | menolak permintaan biasa dengan HTTP 403 |
72
+ | `google_scholar` | deteksi bot aktif, butuh proxy |
73
+
74
+ Sumber yang selalu kosong lebih buruk daripada sumber yang tidak ada, jadi
75
+ yang gagal dibuang **beserta alasannya** — bukan didaftarkan supaya jumlahnya
76
+ terlihat banyak. Deskripsi paket, README, SKILL.md, entri katalog, dan
77
+ `lib/index.js` semuanya menyebut angka yang sama, dan `lib/index.js` punya
78
+ penjaga yang memperingatkan bila jumlahnya berubah tanpa dokumen ikut
79
+ diperbarui.
80
+ > **`iacr` dan `unpaywall` SEMPAT masuk daftar ini lalu dikeluarkan** setelah
81
+ > diuji ulang: `iacr` menjawab HTTP 200 dengan hasil yang bisa diurai, dan
82
+ > `unpaywall` menjawab HTTP 200 dengan DOI. Menganggap "bukan pencari kata
83
+ > kunci" sebagai "mati" adalah kekeliruan yang sudah dikoreksi.
84
+ - **Kegagalan sumber tidak pernah disamarkan sebagai kekosongan.** Setiap
85
+ non-2xx melempar error beserta status HTTP-nya, dan kedua tool memisahkan
86
+ baris `GAGAL` dari baris `OK`. Ini perbaikan langsung atas kelemahan yang
87
+ terukur pada tool sejenis: konektornya mengembalikan daftar kosong untuk
88
+ SETIAP status != 200 tanpa mencatat apa pun, sehingga permintaan yang kena
89
+ batas laju terbaca sebagai "literatur ini tidak ada".
90
+ - **CORE adalah satu-satunya sumber yang WAJIB berkunci.** Terukur: tanpa
91
+ `Bearer` ia menjawab HTTP 429 dengan body **kosong** — bukan 401. Karena itu
92
+ kunci yang hilang dilaporkan sebagai pesan yang bisa ditindaklanjuti, bukan
93
+ dibiarkan menjadi 429 yang membingungkan.
94
+ - Tidak ada kunci API maupun alamat email yang dibawa paket ini.
95
+
96
+ ### Diuji
97
+
98
+ - `node test/uji-sumber.mjs` — **8/8 lulus** melawan API dan halaman SUNGGUHAN;
99
+ **15 dari 16 sumber menjawab**. Dua kegagalan transien terlihat dan
100
+ **dilaporkan apa adanya**, bukan disembunyikan sebagai "0 hasil":
101
+ Semantic Scholar sempat 429 karena pengujian beruntun (pulih setelah 150
102
+ detik), dan PubMed Central sempat HTTP 500 `error forwarding request`
103
+ (batas laju E-utilities; sebelumnya 3 hasil di jalan yang sama).
104
+ - `node test/uji-plugin.mjs` — **29/29 lulus** dengan `ctx` tiruan: dua tool
105
+ terdaftar, provider skill mengembalikan rank 450, frontmatter terurai
106
+ (deskripsi 348 huruf, di bawah batas katalog 500), dan kedua tool benar-benar
107
+ memanggil jaringan.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rasyid (rasyidmmz)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE.md ADDED
@@ -0,0 +1,50 @@
1
+ # NOTICE
2
+
3
+ ## What this package is
4
+
5
+ `dsh-paper-search` is an **independent implementation** for DeepSeek Harness. It
6
+ contains no third-party source code. Every source connector in `lib/sources.js`
7
+ is a small HTTP client written for this package, talking to public REST APIs and
8
+ public web pages directly.
9
+
10
+ ## Upstream relationship
11
+
12
+ This package was **inspired by** [`openags/paper-search-mcp`](https://github.com/openags/paper-search-mcp)
13
+ (MIT licensed) and reuses its naming convention for optional credential
14
+ environment variables (`PAPER_SEARCH_MCP_*`) so that a machine already set up
15
+ for that tool needs no extra configuration. **No code from that project is
16
+ included, vendored, or copied.** If you install the upstream CLI separately,
17
+ this plugin will use it only as an optional extra source; it is never required.
18
+
19
+ ## Data sources and their terms
20
+
21
+ Results come from public APIs and public web pages operated by third parties.
22
+ Each keeps its own terms of use and rate limits. Users are responsible for
23
+ complying with them. Specifically:
24
+
25
+ | Source | Operator | Access |
26
+ |---|---|---|
27
+ | Crossref | Crossref (DOI registration agency) | public REST API |
28
+ | OpenAlex | OurResearch | public REST API, "polite pool" email recommended |
29
+ | DOAJ | Directory of Open Access Journals | public REST API |
30
+ | Europe PMC | EMBL-EBI | public REST API |
31
+ | PubMed | U.S. National Library of Medicine | public E-utilities API |
32
+ | arXiv | Cornell University | public API, 1 request / 3 seconds requested |
33
+ | Semantic Scholar | Allen Institute for AI | public API, key raises rate limits |
34
+ | Garuda | Kemdiktisaintek, Republic of Indonesia | public web pages |
35
+ | Sinta | Kemdiktisaintek, Republic of Indonesia | public web pages |
36
+ | IOS OneSearch | Perpusnas, Republic of Indonesia | public web pages |
37
+ | Moraref | Ministry of Religious Affairs, Republic of Indonesia | public web pages |
38
+
39
+ ## No bundled credentials
40
+
41
+ This package ships **no API keys and no email addresses**. Any credential is
42
+ read at runtime from the user's own configuration (plugin config, environment
43
+ variable, or the upstream tool's `.env` file) and is never transmitted anywhere
44
+ except to the service that issued it.
45
+
46
+ ## What this package does not do
47
+
48
+ - It does not download or redistribute full-text PDFs.
49
+ - It does not bypass paywalls or access controls.
50
+ - It does not send queries to any service other than the ones listed above.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # dsh-paper-search
2
+
3
+ Literature search for **DeepSeek Harness**: sixteen sources behind one tool, all
4
+ native HTTP — no external CLI, no Python package. Fourteen of them need no
5
+ credential at all.
6
+
7
+ Registered as the `paper-search` skill and two model tools (`paper_search`,
8
+ `paper_status`).
9
+
10
+ ## Sources
11
+
12
+ **International (13)** — official JSON APIs, except IACR which is parsed HTML:
13
+
14
+ | id | Source | Notes |
15
+ |---|---|---|
16
+ | `crossref` | Crossref | DOI metadata across publishers, broadest coverage |
17
+ | `openalex` | OpenAlex | metadata + abstracts; an email raises the rate limit |
18
+ | `doaj` | DOAJ | open-access journals — includes many Indonesian journals |
19
+ | `europepmc` | Europe PMC | biomedicine, full text for OA records |
20
+ | `pubmed` | PubMed | biomedicine; two calls per search (esearch + esummary) |
21
+ | `pmc` | PubMed Central | open-access full text; two calls per search |
22
+ | `arxiv` | arXiv | preprints; the plugin self-paces to 1 request / 3 s per arXiv's TOU |
23
+ | `semantic` | Semantic Scholar | a free key raises the rate limit |
24
+ | `core` | CORE | open-access repository aggregator — **CORE_API_KEY required** |
25
+ | `zenodo` | Zenodo | general repository: data, software, preprints |
26
+ | `hal` | HAL | French open-access repository |
27
+ | `iacr` | IACR ePrint | cryptology preprints |
28
+ | `unpaywall` | Unpaywall | **DOI input, not keyword** — resolves a DOI to its open-access copy; **email required** |
29
+
30
+ **Indonesian (3)** — parsed from public pages:
31
+
32
+ | id | Source | Notes |
33
+ |---|---|---|
34
+ | `garuda` | Garuda (Kemdiktisaintek) | Indonesian journal articles |
35
+ | `sinta` | Sinta (Kemdiktisaintek) | accredited **journals**, not articles |
36
+ | `ios` | IOS OneSearch (Perpusnas) | Indonesian repository aggregator |
37
+
38
+ ### `sources: "all"` and the DOI-input source
39
+
40
+ `unpaywall` answers only when the query **is a DOI**. So when you search with
41
+ keywords, it is skipped rather than run and reported as an empty result — and
42
+ the output says so:
43
+
44
+ ```
45
+ CATATAN: unpaywall dilewati karena query ini bukan DOI — sumber itu memang
46
+ mencari dengan DOI, bukan kata kunci.
47
+ ```
48
+
49
+ Pass a DOI and it runs. Pass `sources: "unpaywall"` with keywords and it runs
50
+ too, returning nothing — because that is genuinely how it behaves, and the
51
+ output says that rather than pretending the source is broken.
52
+
53
+ ### Deliberately excluded, with the reason
54
+
55
+ Every exclusion was measured on 2026-09-28. An always-empty source is worse than
56
+ an absent one, so these are dropped rather than registered to look larger:
57
+
58
+ | Source | Reason |
59
+ |---|---|
60
+ | `moraref` | landing page only, no parseable result markup (3,964 characters) |
61
+ | `dblp` | API is behind bot protection — returns "Making sure you're not a bot!", not JSON |
62
+ | `openaire` | test request **timed out at 30 s and again at 40 s** |
63
+ | `biorxiv`, `medrxiv` | their API is **date-range** based, not keyword search |
64
+ | `base` | OAI-PMH requires institutional IP registration (`Access denied for IP address …`) |
65
+ | `citeseerx` | returns HTTP 404, then **hangs** until the request is killed |
66
+ | `ssrn` | rejects ordinary requests with HTTP 403 |
67
+ | `google_scholar` | bot detection active; a proxy is required |
68
+
69
+ ## Install
70
+
71
+ ```sh
72
+ # from npm
73
+ dsh plugin --profile <profile> add dsh-paper-search
74
+
75
+ # or straight from the repository
76
+ dsh plugin --profile <profile> add github:rasyidmmz/dsh-paper-search
77
+ ```
78
+
79
+ The package ships a `dsh.bundle.patch`, so DSH installs its loader row
80
+ automatically. **Do not also write an `id: paper-search` row by hand** — the
81
+ loader throws `duplicate loader entry id` rather than warning.
82
+
83
+ Restart DSH after installing.
84
+
85
+ ## Use
86
+
87
+ ```
88
+ paper_search({ query: "machine learning" })
89
+ paper_search({ query: "pendidikan karakter", sources: "indonesia" })
90
+ paper_search({ query: "CRISPR", sources: "pubmed,europepmc,doaj", max_results: 10 })
91
+ paper_search({ query: "digital literacy", open_access_only: true, year_from: 2020 })
92
+ paper_status() # which sources answer right now
93
+ paper_status({ probe: false }) # configuration only, no network
94
+ ```
95
+
96
+ For Indonesian topics, **use Indonesian keywords**: `"pendidikan karakter"`
97
+ returns real Indonesian journals, while the English equivalent returns
98
+ international results that do not match the intent.
99
+
100
+ ## What makes the reporting honest
101
+
102
+ Every result separates **sources that failed** from sources that answered:
103
+
104
+ ```
105
+ v Crossref 3 results (2316 ms)
106
+ x Semantic Scholar FAILED — HTTP 429 — {"message": "Too Many Requests...
107
+ v Garuda 3 results (1428 ms)
108
+ ```
109
+
110
+ A source that fails is never allowed to masquerade as "no results". This is not
111
+ a detail: a widely-used tool in this space returns an empty list for *every*
112
+ non-200 response with no error recorded at all, so a rate-limited request reads
113
+ as "this literature does not exist". Here, the failure and its HTTP status are
114
+ printed, and `paper_status` exists to prove liveness on demand.
115
+
116
+ ## Credentials (all optional)
117
+
118
+ No key is bundled. Read order:
119
+
120
+ 1. plugin config in your DSH profile,
121
+ 2. environment variables `PAPER_SEARCH_MCP_<NAME>`, then `<NAME>`,
122
+ 3. the file `~/.config/paper-search-mcp/.env` (belonging to the upstream CLI).
123
+
124
+ | Name | Purpose |
125
+ |---|---|
126
+ | `UNPAYWALL_EMAIL` | any email — used as the Crossref/OpenAlex "polite pool" contact |
127
+ | `OPENALEX_EMAIL` | same, OpenAlex only |
128
+ | `SEMANTIC_SCHOLAR_API_KEY` | raises Semantic Scholar rate limits |
129
+ | `DOAJ_API_KEY` | raises DOAJ's hourly limit |
130
+ | `CORE_API_KEY` | **required for the `core` source** — without it CORE answers HTTP 429 with an empty body |
131
+
132
+ Fourteen of the sixteen sources work with no credential at all; keys raise rate
133
+ limits and stability. **Two are exceptions, and each is reported as a clear,
134
+ actionable failure rather than a confusing 429:**
135
+
136
+ - `core` needs `CORE_API_KEY`.
137
+ - `unpaywall` needs an email (any real address) — it also needs a DOI as input.
138
+
139
+ ## What this package does not do
140
+
141
+ - It does not download or redistribute full-text PDFs.
142
+ - It does not bypass paywalls or access controls.
143
+ - It does not call any service other than the ten listed above.
144
+ - It does not require or install `paper-search-mcp`. If that CLI happens to be
145
+ installed, you can use it for its additional sources (CORE, Zenodo, HAL,
146
+ SSRN, Unpaywall, OpenAIRE, CiteSeerX, BASE) — this plugin never depends on it.
147
+
148
+ ## Licence and attribution
149
+
150
+ MIT — see [LICENSE](LICENSE). Attribution and source terms are in
151
+ [NOTICE.md](NOTICE.md).
@@ -0,0 +1,17 @@
1
+ # Patch bundle dsh-paper-search.
2
+ #
3
+ # Bentuk ini DISALIN dari bundle yang sudah terbukti terpasang di profil
4
+ # (`dsh-ai-memory/cordis.patch.yml`) — bukan dikarang:
5
+ #
6
+ # - insert:
7
+ # - id: <id>
8
+ # name: <nama paket npm>
9
+ #
10
+ # `id` harus UNIK. Loader melempar `duplicate loader entry id` bila id yang sama
11
+ # muncul dua kali, dan itu terjadi SEBELUM aplikasi sempat memperingatkan.
12
+ #
13
+ # `path` TIDAK ditulis di sini: entri `insert` hanya menyebut nama paket, dan DSH
14
+ # menyelesaikan berkas masuknya dari `main`/`exports` package.json paket.
15
+ - insert:
16
+ - id: paper-search
17
+ name: dsh-paper-search