Skip to content
dsh.fish
Bundle

dsh-listen-npm

npm package monitor plugin (dual-face): query full npm package info with daily install counts, and watch your own packages for version / download changes. npm 包监控插件:查询 npm 包完整信息与每日安装量,监控列表自动跟踪版本与下载量变化。Bilingual UI (zh/en)

Source
jsoncode
License
MIT
Updated
Updated 4 hours ago

Readme

# dsh-listen-npm

<p align="center">
  <img src="assets/logo.svg" alt="dsh-listen-npm logo" width="72" />
</p>

**dsh-listen-npm** is an npm package monitor plugin for DeepSeek Harness (DSH).
Query any npm package's full info — with **daily install counts** highlighted — and
watch your own packages for version / download changes.

- **Full package info**: latest version, dist-tags, description, license, author,
  maintainers, links, publish times, versions, dependencies, README excerpt
- **Daily install counts**: day / 7-day / 30-day / year download stats with a
  30-day daily chart (daily granularity comes straight from the official
  `api.npmjs.org/downloads/range` API)
- **Watch list**: add packages to monitor; the plugin polls on a configurable
  interval, records snapshots and version changes (see the History tab), and shows
  download trends
- **Model tools**: `dsh_npm_info`, `dsh_npm_downloads`, `dsh_npm_watch`
- **Bilingual UI** (zh/en), follows the host language

[中文文档](README.zh.md)

## Preview

A resident **npm Monitor** button sits in the sidebar footer (right above the
settings area) with a capsule for the watch count. The
modal has three tabs: **Query** (search + full detail), **Watch**, **History**.

## Features

- **Query tab** — type a package name (or paste an npmjs.com link): debounced
  suggestions from the registry search API, full detail on Enter / click:
  - Download stats card: **latest day / last 7 days / last 30 days / last year**
    (latest reported day is the hero stat; when npm's data lags, the label shows
    the real date plus the lag in days) plus a 30-day daily bar chart with peak,
    average line, and per-bar tooltips
  - Basic info: first publish, last update, latest publish (+publisher), total
    versions, unpacked size, file count, node engines, maintainers
  - dist-tags chips, recent version list (fixed-height scroll), README excerpt
    (rendered as Markdown)
  - One-click **Watch** button
- **Watch tab** — add/remove packages; each row shows the latest version (a plain
  version number, no "new version" marker), latest-day/7-day downloads with trend vs
  the previous snapshot, a mini bar chart of the last 7 days of daily installs
  (days npm has not reported yet show as grey placeholder bars, keeping the dates
  continuous), and the last-check time
- **History tab** — snapshot timeline per package (recorded on watch-add and
  version changes): time, version, latest-day/week downloads, change type
- **Entry visibility** — the sidebar footer entry follows a **Show in menu**
  preference (default on), toggled from **Settings → npm Monitor** or the top of
  the Watch tab. When off the entry renders nothing; the host settings page keeps
  an **Open npm Monitor** button so the modal stays reachable (both places share
  one preference source and stay in sync).
- **Dialog palette** — the modal follows dsh-get-balance: a `rgba(0,0,0,.32)` scrim
  with `blur(12px) saturate(1.2)`, a `color-mix(bg-layer-1 78%)` glass panel with a
  `border-l2` hairline and 14px radius, `border-l1` dividers, solid
  `button-primary-fill` primary buttons, a **semi-transparent** `button-primary-fill 18%`
  capsule (plus a 30% outline) for the active tab — whitish in dark theme, and unchanged
  on hover once selected — `bg-base` inputs and search
  popover, `bg-layer-2` cards, and `state-*` tokens for status colours.
- **Background polling** — the poller runs decoupled from the modal; every
  `refreshMinutes` (host config, default 10) it refreshes the whole watch list
  with 1 lightweight `dist-tags` request + 1 `range/last-week` request per
  package (latest-day / 7-day totals and the mini chart's daily series are both
  derived from it); a new version is recorded into the snapshots / History
  timeline with no in-UI notification
- **Data files** — watch list and snapshots persist to
  `$DSH_HOME/dsh-listen-npm.json` (atomic writes, `.bak` on corruption)
- **HTTP API** — the browser half talks to `/dsh-listen-npm/api`
  (trust-fenced POST JSON), so polling never produces command nodes in the
  conversation; falls back to the command channel on older hosts
- **Model tools** — `dsh_npm_info` / `dsh_npm_downloads` / `dsh_npm_watch`
  (list / add / remove / refresh)

## Configuration (host config)

```yaml
registryUrl: https://registry.npmjs.org   # swap for a mirror, e.g. https://registry.npmmirror.com
downloadsUrl: https://api.npmjs.org/downloads
refreshMinutes: 10                        # watch list auto refresh interval
```

## Data notes

- Daily granularity comes from `api.npmjs.org/downloads/range`; npm aggregates
  downloads per day with a T+N delay (3-4 days in practice), so **the last day
  with data is not necessarily yesterday**.
- **Continuity**: every series is padded through "yesterday" (local time) — days the
  API skips inside its window become 0, and days npm has not reported yet become 0
  flagged `pending` (drawn as grey dashed bars, capped at 14 days), so the date axis
  never stops a few days short. Padded days exist only to keep the calendar
  continuous.
- **Aggregation**: latest-day / last-7-day / last-30-day totals count **real data
  days only** (window ending on the last day with data), so npm's reporting lag can
  never deflate them into fake zeros; the tiles show the actual cut-off date (the
  label reads "Yesterday" only when the data really is that fresh) plus a line
  naming the days npm has not reported yet.
- Scoped packages (`@scope/name`) are supported everywhere; the registry path
  URL-encodes the slash while the downloads API uses it raw.
- The full registry doc is fetched once per query (react ≈ 7 MB) — the curl
  collector caps at 32 MB.

## File structure

```
├── src/host/*.ts         # host half: index.ts (entry), npm.ts (curl core), ops.ts (op dispatch), store.ts, fence.ts, types.ts
├── src/shared/*.ts       # pure logic shared by both halves (daily.ts: calendar padding)
├── src/client/*.tsx      # browser half (React TSX): plugin.tsx, i18n, styles, rpc, store, poller, components/*
├── lib/index.js          # host half build artifact (tsdown, ESM), committed for git installs
├── lib/client.js         # browser half build artifact (tsdown → __ModuleLoader__ factory), committed
├── lib/types/            # type declarations (tsc -b)
├── scripts/              # verify-client.mjs (host seed-table simulation)
├── tsdown.config.ts      # tsdown config (node half + client bundle banner wrap)
├── tsconfig.json         # solution: tsconfig.host.json / tsconfig.client.json
├── cordis.patch.yml      # bundle patch (plugin row by package name)
├── package.json          # dsh.bundle + dsh.client(web) manifest + peerDependencies
├── README.md             # this file
└── README.zh.md          # Chinese docs
```

## Install

```sh
# local development
dsh plugin --profile web add ./dsh-listen-npm

# published: npm / tarball / GitHub
dsh plugin --profile web add dsh-listen-npm
dsh plugin --profile web add ./dsh-listen-npm-0.1.0.tgz
dsh plugin --profile web add github:you/dsh-listen-npm#<sha>

dsh --profile web --dump-config   # verify the config layer
dsh --profile web                 # start (host half needs a restart)
```

> **Local dev dependencies**: the host loads `lib/index.js` with native Node ESM
> resolution for `@deepseek-ai/schemastery`, `@deepseek-ai/dsh-tools`,
> `@deepseek-ai/dsh-settings`, so the plugin dir must contain a resolvable
> `node_modules` (gitignored). Either run `pnpm install` here, or link the host's
> flattened packages:
>
> ```powershell
> New-Item -ItemType Directory "$PWD\node_modules\@deepseek-ai" -Force
> foreach ($p in 'schemastery','dsh-tools','dsh-settings') {
>   New-Item -ItemType Junction "$PWD\node_modules\@deepseek-ai\$p" -Target "$env:DSH_HOME\profiles\node_modules\@deepseek-ai\$p"
> }
> ```

## Build

```sh
pnpm install
npm run check    # tsc -b (both programs)
npm run build    # tsc -b && tsdown (lib/index.js + lib/client.js)
npm run verify   # simulate the host loading lib/client.js
```

## License

MIT

Install

dsh plugin --profile web add github:jsoncode/dsh-listen-npm

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source