Skip to content

Releases

v0.4.1 — plugin icon, and collapsed-by-default settings sections

Choose a tag to compare

@Astervolans Astervolans released this 07 Oct 05:25

The plugin now has an icon

package.json declares a top-level "icon": "./icon.svg", and the file ships in
files. DSH reads it without running any plugin code and renders it in the
bundle's card (36 px), its detail page header (36 px), and its component rows
(30 px inside a 40 px frame) — in the Plugins panel and in Settings.

Two framing fixes were applied to the Affinity export: the root viewBox is
rewritten to the smallest square box centred on the artwork (a non-square
viewBox letterboxes inside the panel's object-fit: contain box and renders
smaller), and the external DTD reference is dropped — an <img> neither parses
nor fetches it. The drawing itself is untouched.

test/config.test.mjs guards every rule the Host enforces, because each one fails
silently at runtime: a bad icon keeps the title and description and only adds a
metadata diagnostic, and an undecodable image falls back to the built-in artwork.

The three settings sections start collapsed

0.4.0 seeded all three open, on the theory that the page exists to make keys and
backends reachable. In practice the page is read far more often than it is edited,
and an expanded form buries the thing a reader actually wants — the two status
badges saying whether a key is configured and which Scholar backend is live.

It now reads as a table of contents first: three headers carrying their badges,
and you open only the group you came to change. The disclosure contract is
unchanged — the header is still the <button> carrying aria-expanded, a
collapsed section still renders no fields, and the badge still stays in the header.

Upgrading

dsh plugin --profile desktop add @astervolans/dsh-literature-search

then restart DSH (plugin metadata is read at host start, so the icon needs the
restart). Versions ≤ 0.3.2 are rejected at install time on DSH 0.2.0.

Verification

Offline suite, 75 cases: MEDLINE parsing 7, Scholar parsing/paging 6, plugin
and tools 24, settings routes 17, client bundle 15, config drift 6. No network,
stub fetch.

The icon was confirmed end-to-end in a standalone dsh web instance: the
Plugins panel card, the plugin page header and the component row all render it
from the manifest's data URL at 36 / 36 / 30 px, and the three sections report
aria-expanded="false" with their badges visible and no fields in the tree.

v0.4.0 — configuration moves to the plugin page, in three collapsible sections

Choose a tag to compare

@Astervolans Astervolans released this 07 Oct 03:01

One level up: the configuration is now on the plugin's own page

0.3.3 put the page on the bundle's row, so reaching it meant opening
@astervolans/dsh-literature-search and then clicking the literature-search
row — a control nested inside a list of components, for a form that configures the
package as a whole.

It now registers plugins.bundle.config, keyed by the bundle's package name.
The plugin manager renders that cell in PackageDetail, between the package's
one-liner and its row list — so the configuration is the first thing on
插件 → @astervolans/dsh-literature-search, above the rows.

Nothing depends on the patch row id any more. cordis.patch.yml, the
/plugin/literature-search route tree, the literature-search settings
namespace, the credential slots and every saved key are unchanged.

Three collapsible sections

PubMed, Google Scholar and 通用 are now three expandable sections. Each
header is a real <button> — the whole strip is the hit target, and it is
keyboard-reachable with the browser's own focus ring — and a collapsed section
does not render its fields at all.

The status badge stays in the header, so a folded section still tells you whether
its key is configured.

All three start expanded: the page exists to make keys and backends reachable,
and collapsing everything by default would hide exactly that.

Upgrading

dsh plugin --profile desktop add @astervolans/dsh-literature-search

then refresh the page (restart DSH if the section does not appear). Versions
≤ 0.3.2 are rejected at install time on DSH 0.2.0
(Plugin … is incompatible with dsh 0.2.0-rc.2) — 0.3.3 fixed that.

Verification

Offline suite, 73 cases: MEDLINE parsing 7, Scholar parsing/paging 6, plugin
and tools 24, settings routes 17, client bundle 15, config drift 4. No network,
stub fetch.

The client suite now drives the disclosure directly: the header carries
aria-expanded, a collapsed section renders no fields but keeps its badge, and
the page seeds all three sections open. It also pins the new key against
package.json#name — the manager dispatches by package name, and a key it never
dispatches renders nothing and raises nothing.

v0.3.3 — configuration page moves to the Plugins panel

Choose a tag to compare

@Astervolans Astervolans released this 06 Oct 18:41

Configuration page moved to the Plugins panel

The settings page used to register a tab into settings.plugins.tab, which put it
next to the built-in inventory under Settings → Built-in plugins — a section
whose only job is to report what a deployment ships. DSH's plugin manager is
explicit about the split: the sidebar 插件 / Plugins panel is where plugins are
configured, and "内置插件列表及运行状态可在「设置 → 内置插件」中查看".

The page now registers into plugins.row.config — the keyed slot
@deepseek-ai/dsh-client-ui-plugin-manager declares for a row a bundle owns —
with the key `${package name}#${row id}`,
@astervolans/dsh-literature-search#literature-search here. That is the same
string the panel recomputes through its own rowConfigKey(bundle, rowId) to decide
whether a row gets a configure control, so the page now hangs off the row itself:

插件 → @astervolans/dsh-literature-search → the literature-search row

The slot owner renders the entry twice, so the bundle now serves
PluginConfigViewProps.view: a hook-free one-liner for summary (the row's
description fallback) and the full form for page.

Nothing else moved — the /plugin/literature-search route tree, the
literature-search settings namespace, the credential slots and every saved key
are unchanged.

DSH 0.2.0 no longer disables the plugin

Also in this release (prepared as 0.3.3 on 2026-09-29, never published until now).

Starting with DSH 0.2.0, @deepseek-ai/dsh-app-boot's
evaluatePluginCompatibility() pre-flights every profile row and disables the
whole row when a @deepseek-ai/dsh / @deepseek-ai/dsh-* peer fails against the
running runtime. Both declared peers were ^0.1.0-rc.6, whose semantic upper
bound is <0.2.0 — so on a 0.2.0 host the bundle layer was skipped entirely: no
pubmed_* / scholar_* tool, no prompt-guidance section, no route tree.

They are now >=0.1.7-rc.1 <0.3.0, covering the 0.1.7 line and the whole
0.2.0 line. @deepseek-ai/cordis and @deepseek-ai/schemastery are not gated
and keep their ranges.

Upgrading

dsh plugin --profile desktop add @astervolans/dsh-literature-search

then restart DSH. Versions ≤ 0.3.2 are rejected at install time on DSH 0.2.0
(Plugin … is incompatible with dsh 0.2.0-rc.2).

Verification

Offline suite, 71 cases: MEDLINE parsing 7, Scholar parsing/paging 6, plugin
and tools 24, settings routes 17, client bundle 13, config drift 4. No network,
stub fetch. The client bundle suite now pins the slot name, the key against both
package.json and cordis.patch.yml, and the summary/page split — a key mismatch
costs the row its configure control without raising anything, so a test is the
only place it can be caught before a user finds a row that will not open.

0.3.2

Choose a tag to compare

@Astervolans Astervolans released this 28 Sep 07:58

Fixed

  • A cleared key slot still read as "已配置(保存在设置中)". collectSlot treated the settings descriptor's secret metadata (secrets[].set) as proof of a stored key, but the host reports set: true for any role('secret') field merely present in the projected value — and this namespace declares pubmedApiKey / scholarSerpApiKey as default(''), so the flag was already true before a key was ever entered and stayed true after a clear. A non-empty inline value — what resolveSecret actually reads — now decides the settings-inline state; the flag stays in facts for the diagnostic line, which prints settings-secret=set or settings-secret=declared/empty.

  • Clearing a slot left an inline key live. POST /credential skipped the inline-field cleanup whenever clear: true, so a value in pubmedApiKey / scholarSerpApiKey — which outranks the credential store in resolveSecret — survived the clear while the card reported the slot as cleared. A clear now empties both layers.

Added

  • Regression tests in test/web.test.mjs: a declared-but-empty secret slot is not configured, a genuinely set inline key still reports the settings-inline state, and a clear with an inline key present writes it away.

Full changelog: https://github.com/Astervolans/dsh-literature-search/blob/main/CHANGELOG.md

0.3.1 - client bundle registers under the scoped package name

Choose a tag to compare

@Astervolans Astervolans released this 28 Sep 05:54

@deepseek-ai/dsh-client-modules keys a client boot-graph row by the package name the row resolved to and arrives it on that id. 0.3.0 renamed the npm package to @astervolans/dsh-literature-search but left lib/client.js registering the pre-rename dsh-literature-search, so the row never arrived, the loader retried the bundle on its one-resource URL, and the second execution aborted the page:

Uncaught Error: client-modules: duplicate factory registration for "dsh-literature-search" (bundle executed twice without invalidate?)
web boot: 1 entry did not activate
@astervolans/dsh-literature-search: import failed (see console for the import error)

The desktop host then marked the profile unhealthy, wrote the plugin into desktopNextDeselectedBundles ("Disabled bundle for desktop"), and only started healthy with the plugin switched off -- which is what made the install look like DSH would not start.

Fixed

  • lib/client.js now registers under package.json#name, the convention the loader itself follows (its own bootstrap bundle registers @deepseek-ai/dsh-client-modules). The Settings -> Plugins literature card loads again.

Added

  • Client-registration contract tests in test/client.test.mjs: one asserts the registration id equals package.json#name, the other replays the loader's two load-bearing rules (a row is keyed by package name; a second registration throws) against the real bundle. Both cases fail on the shipped 0.3.0 bundle and pass on this fix, verified locally.

Changed

  • cordis.patch.yml, both READMEs and the CHANGELOG now state which identifiers stay unscoped and which two must be the scoped name. The 0.3.0 changelog entry, which claimed the client module id had been left alone, carries a correction note.

Upgrading from 0.3.0: reinstall with dsh plugin --profile desktop add @astervolans/dsh-literature-search@0.3.1 (or re-run install.ps1), then restart DSH. Settings and saved API keys are untouched.

Full changelog: https://github.com/Astervolans/dsh-literature-search/blob/v0.3.1/CHANGELOG.md

v0.3.0 — scoped package name and GitHub Packages publishing

Choose a tag to compare

@Astervolans Astervolans released this 27 Sep 13:03
1f1c126

The npm package is renamed to the scoped name @astervolans/dsh-literature-search, and releases now publish to GitHub Packages alongside npm.

Why the rename

GitHub Packages only accepts scoped npm packages, so the unscoped dsh-literature-search could never be published there. Rather than let the two registries drift apart under different names, 0.3.0 adopts the scoped name everywhere — one install command, both registries:

dsh plugin --profile desktop add @astervolans/dsh-literature-search

What did not change

The plugin's runtime identity is deliberately untouched, so an existing profile keeps its configuration and its saved keys:

unchanged value
plugin name literature-search
settings namespace literature-search
routes /plugin/literature-search
client module id dsh-literature-search
diagnostics directory $DSH_HOME/.dsh-literature-search
NCBI tool parameter dsh-literature-search

What did have to move

cordis.patch.yml's row name is the module specifier the cordis loader imports from the profile directory, where only the scoped package exists — an unscoped value there makes dsh web abort at startup with Cannot find package. It is now "@astervolans/dsh-literature-search" (quoted, because a leading @ is a YAML reserved indicator). test/config.test.mjs already asserts row.name === package.json.name, so the two cannot diverge silently again.

install.ps1 / uninstall.ps1 copy to node_modules\@astervolans\dsh-literature-search. install.ps1 rewrites a pre-0.3.0 unscoped row in place, and uninstall.ps1 also removes a pre-0.3.0 copy and prunes the scope directory once it empties.

Upgrading from 0.2.x

dsh plugin --profile desktop remove dsh-literature-search
dsh plugin --profile desktop add @astervolans/dsh-literature-search

Then restart DSH. Nothing else to change.

GitHub Packages publishing

.github/workflows/release-package.yml follows the GitHub Packages quickstart — a build job gates publish-gpr on release: created — and publishes with the workflow's own GITHUB_TOKEN: no secret, no token rotation. It also carries workflow_dispatch, and re-running an already-published version is a no-op rather than a red run.

Only the @astervolans scope is remapped in CI, so npmjs.org stays the default registry and the same scope can still be published there from a local clone. Installing from GitHub Packages does need a token even though the package is public:

@astervolans:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_PAT

On npmjs.org nothing changes; publishConfig.access=public is declared, which a scoped package needs there.

Fixed

  • npm pack --json shape drift in the CI packaging gate. npm ≤ 11 emits an array and npm ≥ 12 an object keyed by package name, so the package job's require('./pack.json')[0] would have thrown a TypeError on a runner with a newer npm. Both the package job and the new release workflow now accept either shape.

Full diff: v0.2.4...v0.3.0

v0.2.4 — fix npm page showing the Chinese README

Choose a tag to compare

@Astervolans Astervolans released this 13 Sep 14:52

Fixes the npm package page showing the Chinese README instead of the English one.

What went wrong

npm chooses the package readme by globbing {README,README.*} in the package
root and taking the first result whose name ends in a markdown extension. In
that glob's order:

["README.zh-CN.md", "README.md"]

README.zh-CN.md sorts first, so the 0.2.3 publish shipped the translation
as the package readme and README.md was ignored entirely. The file was in the
tarball and correct — npm simply never looked at it.

This is a display-only defect: the shipped code in 0.2.3 is unchanged and
worked correctly.
Only the npm page rendered the wrong language.

The fix

The Chinese README now lives at docs/README.zh-CN.md. npm's glob is not
recursive, so it no longer matches, and the selection is unambiguous. Verified
against the same glob and selection logic npm itself uses rather than by
assumption:

glob order: ["README.md"]
=> npm will pick: README.md

Moving the file broke its relative links (it now reaches LICENSE,
package.json, CHANGELOG.md, CONTRIBUTING.md and SECURITY.md through
../, and API-RESEARCH.md is a sibling). All of them were repaired, and every
relative link in both READMEs was re-checked and resolves.

CONTRIBUTING.md now documents the trap along with the command to re-check the
choice before publishing, so a future translation cannot reintroduce it.

Package contents

15 files. package.json (with its dsh.bundle.patch declaration) and
cordis.patch.yml are both included, as before.

Full diff: v0.2.3...v0.2.4

v0.2.3 — first npm release, English README

Choose a tag to compare

@Astervolans Astervolans released this 13 Sep 14:45

First release published to npm, so installing no longer needs a script:

dsh plugin --profile desktop add dsh-literature-search

dsh plugin is a thin pnpm forwarder that reconciles the profile's bundle layer
list by installed state, so a dependency declaring dsh.bundle joins the stack
on its own. This is also what gives the plugin an install command in the
1024 Store catalog, which lists store installs from
npm only.

Added

  • English README as the primary README.md, with the Chinese original kept
    in full as README.zh-CN.md. The two link to each other at the top. npm
    renders README.md to the package page, which is why English leads.
  • docs/ and README.zh-CN.md now ship in the package, so the links the README
    makes resolve for npm consumers instead of only for a git clone.
  • dsh-plugin keyword, matching the GitHub topic used for catalog discovery.

Changed

  • The DSH peer dependencies are now optional via peerDependenciesMeta.
    They are supplied by the DSH runtime; without this, npm tries to resolve their
    own peer graph during install and fails with an ERESOLVE conflict on
    @deepseek-ai/dsh-tools. Marking them optional keeps
    npm install dsh-literature-search working while preserving the declaration
    for hosts that do resolve them.
  • The README install section leads with the npm command and keeps install.ps1
    as the documented offline route.

Security

  • npm publish is gated behind prepublishOnly, which runs the full offline
    suite first, so a broken package cannot be published by accident.

Package contents

15 files, 60.1 kB packed / 183.9 kB unpacked. Both package.json (with its
dsh.bundle.patch declaration) and cordis.patch.yml are included — the
harness needs the patch, and the catalog's detection reads the declaration.

Full diff: v0.2.2...v0.2.3

v0.2.2 — dark-mode settings card fix

Choose a tag to compare

@Astervolans Astervolans released this 13 Sep 13:58

Patch release fixing a dark-mode legibility bug in the settings card.

Fixed

  • The settings card was unreadable in dark mode. The primary buttons paired
    a --dsw-alias-brand-primary fill — which is near-white (#f9fafb) in dark
    mode — with a literal #fff label, so the text disappeared into the button.
    They now use the first-party pairing --dsw-alias-label-primary fill over
    --dsw-alias-bg-layer-3 label; those two tokens swap together per theme.
  • Status colours come from the --dsw-alias-state-{success,warn,error}-* tokens
    instead of hardcoded hex, so badges and hints survive a theme switch. The
    non-existent --dsw-alias-label-error token is no longer referenced.

Changed

  • Secondary buttons use the first-party ghost treatment: transparent fill, a
    --dsw-alias-border-l2 outline and the secondary label colour.
  • Disabled buttons use the first-party 40% opacity, down from 50%.
  • CI: actions/checkout and actions/setup-node bumped to v6, which runs on the
    Node 24 runtime and clears the Node 20 deprecation annotations.

Tests

The offline suite is now 62 cases (client bundle 6 → 10). The new
deep-render helpers expand function components the way React would, so nodes
owned by child components — the credential fields' buttons — are reachable from
a test. The added cases guard the theme regressions directly: primary buttons
must use the paired tokens, no style may paint --dsw-alias-brand-primary
behind a fixed colour, disabled buttons must sit at 0.4 opacity, and status
colours must come from state tokens rather than literals.

Full diff: v0.2.1...v0.2.2

v0.2.1 — first public release

Choose a tag to compare

@Astervolans Astervolans released this 13 Sep 13:55

First public release of dsh-literature-search — a DeepSeek Harness (dsh) plugin
that searches literature through the official PubMed NCBI E-utilities API and
Google Scholar, and returns both backends as one shared paper structure.

Tools

Tool Upstream Notes
pubmed_search esearch + esummary + efetch Field-tagged queries, sort, date windows, offset paging
pubmed_paper efetch + esummary Full record: abstract, MeSH, keywords, publication types
pubmed_related elink pubmed_pubmed NCBI related-article ranking
scholar_search SerpApi / HTML Cited-by counts, PDF links, year window, paging
scholar_cite SerpApi MLA / APA / Chicago / Harvard / Vancouver / BibTeX

Highlights

  • PubMed over the official API — free, keyless, documented, rate-limited
    correctly (350 ms without a key, 110 ms with one), with retry/backoff that
    honours Retry-After.
  • Google Scholar has no official API. With a SerpApi key it uses the
    google_scholar engine; without one it parses scholar.google.com HTML and
    reports throttling or anti-bot pages as explicit errors rather than
    silently returning nothing.
  • Zero runtime dependencies — only the Node built-in fetch/AbortSignal
    plus the @deepseek-ai/* peers DSH itself provides. No npm install needed
    to install the plugin.
  • Settings page under Settings → Plugins → Literature Search: credential
    slots, backend switching, rate/result limits, and a one-click connectivity
    test for both backends.
  • 58 offline test cases covering MEDLINE parsing, Scholar parsing/paging,
    plugin and tool contracts, settings routes, the client bundle and config
    drift. The suite uses stub fetch and never touches the network.

Install

powershell -ExecutionPolicy Bypass -File install.ps1 -Profile desktop

Then restart DSH. See the README for configuration and the full option table.

Requirements

Node ^22.19.0 || >=24.0.0. Windows install scripts are included; the plugin
itself is platform-independent.

Known limitations

  • PubMed date filtering uses the print publication date (pdat), so
    ahead-of-print records can show a later pubdate. That is upstream behaviour.
  • The Scholar HTML backend is best-effort and can be blocked by Google; a
    SerpApi key is the reliable option.
  • PubMed abstracts may be copyright-protected — NCBI's disclaimer asks users to
    respect the rights holders, and bulk mining should use local PubMed copies.