Skip to main content
J

dsh-model-list

jsoncode/dsh-model-list

OpenRouter free-models browser plugin (dual-face): fetch OpenRouter models, filter the free ones (price 0 or :free), list them newest-first in a Settings tab with local search and full model info, and create a ready-to-use DSH model config (settings.yaml

Install

dsh plugin --profile web add github:jsoncode/dsh-model-list

README

dsh-model-list

dsh-model-list logo

dsh-model-list is a DeepSeek Harness (DSH) plugin that browses OpenRouter's model directory (the official openrouter.ai/models catalog) and adds any model — free variants included — to your DSH model list in one click.

  • Model Hub entry (设置 → 模型聚合平台 / Model Hub): the Settings entry hosts one tab per provider platform (OpenRouter today; the PLATFORMS registry makes future providers a one-line addition). All fetch logic runs host-side; the tab is the single entry point.
  • Show-in-menu toggle: a slide switch at the very top of the platform page. When on, a 【模型聚合平台 / Model Hub】 entry button appears in the host sidebar's footer action area and opens the same directory in a large overlay. The preference persists in localStorage (default off).
  • Dialog palette: the overlay and dialogs follow 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, 14px radius and a 0 16px 48px rgba(0,0,0,.28) shadow, border-l1 dividers, solid button-primary-fill for primary buttons and active tabs / sub-tabs / filter chips, bg-base inputs, bg-layer-2 cards, and state-* tokens for status colours.
  • Official directory, 1:1: the host fetches the official frontend endpoint https://openrouter.ai/api/frontend/v1/models/find?active=true&fmt=cards&order=newest — once with variant=standard and once with variant=free (both return one card per model, no pagination) — and joins each card's weekly token usage from the response's data.analytics. The browser renders the official filter set client-side: search, sort (weekly usage / newest / context size / input price / output price / name), input & output modalities, prompt-pricing buckets (Free / <$1 / $1–$2 / $2–$5 / $5–$10 / >$10 per M tokens), context-length buckets (<8K / 8K–32K / 32K–128K / 128K–1M / >1M), capabilities (tools / reasoning / structured outputs), series and providers. Cards follow the official anatomy: author avatar, name, weekly usage, provider, description, and spec chips (context, max output, input/output price per M tokens, modality arrows, capability badges).
  • Full info per card: id (click to copy), provider, quantization, created date, supported parameters, moderated / forced-reasoning flags, upstream warnings, expandable full description. Layout adapts to narrow panels and mobile widths.
  • "Add to model list" → one-click model config: the dialog takes an OpenRouter API token only when the platform has no stored credential yet (live-checked via GET /api/v1/key on demand). Confirming then
    1. stores the token through the host credentials service (.credentials.yaml, OPENROUTER_API_KEY),
    2. merges the model into the OpenRouter route in settings.yaml (llm-pi-ai namespace — reuses the existing openrouter / openrouter-free / openrouter.ai route when one exists, preserving its apiKeyEnv and model entries verbatim; otherwise creates openrouter-free with the openai-completions protocol and https://openrouter.ai/api/v1), and
    3. optionally sets it as the default model for new sessions (agent-default-model namespace). The model is selectable in the chat composer's model selector immediately — no host restart.
  • Robust fetching: every OpenRouter request retries up to 10 times (exponential backoff + jitter); a failed refresh degrades to the cached list with a stale notice. Results are cached in $DSH_HOME/dsh-model-list.json (6-hour freshness window).
  • Bilingual UI (中文 / English), no hardcoded paths.

中文文档

Commands

ScriptWhat it does
npm run checktsc -b typecheck (host + client programs)
npm run buildclean lib/, tsc -b, then tsdown bundles lib/index.js (host, ESM) + lib/client.js (browser, __ModuleLoader__ factory)
npm run watchtsdown watch mode
npm run verifysimulate the host module loader against lib/client.js (factory shape + seed externals)
npm run test:opsoffline host-logic tests (find/cards normalization, weekly-usage join, route detection, config-write orchestration, store round-trip incl. v1-cache rejection)
npm run smokelive smoke test against the real OpenRouter models/find endpoint
npm run releasecheck + build + verify, then npm version patch and git push --follow-tags
npm run publish:npmcheck + build + verify, then npm publish --access public

Installation

# Local development (link the workspace; picks up rebuilds of lib/ automatically)
dsh plugin --profile web add ./dsh-model-list

# Published: npm / tarball / GitHub
dsh plugin --profile web add dsh-model-list
dsh plugin --profile web add github:you/dsh-model-list#<sha>

dsh --profile web --dump-config   # verify the layer
dsh --profile web                 # start (restart required for the host half to load)

Local development dependencies: the host loads lib/index.js through native Node ESM, so @deepseek-ai/schemastery and @deepseek-ai/dsh-settings must be resolvable from the plugin directory. Either run pnpm install inside the plugin directory, or use the committed lib/ build (this repository commits lib/, mirroring dsh-jenkins).

Usage

  1. Open Settings → 模型聚合平台 / Model Hub (or enable 在菜单中显示 / Show in menu at the top of the page and use the 【模型聚合平台】 button at the bottom of the host sidebar), pick the platform tab (OpenRouter).
  2. The directory arrives from cache when fresh (hit 刷新 to force a refetch). Use the search box, the variant dropdown, the output-modality tabs, the sort dropdown (weekly usage / newest / context / prices / name), and the filter groups (input modalities, discount, context length, prompt & output pricing, series, supported parameters, providers, model authors) — the official models-page set.
  3. Click 添加到模型列表 / Add to model list on a card:
    • the token field appears only when the platform has no stored credential yet (otherwise the existing OPENROUTER_API_KEY is reused and noted in the dialog),
    • optionally tick 设为新会话的默认模型,
    • confirm — the config is written and the dialog shows the exact route/model to pick in the composer's model selector.

Adding a platform

  1. src/client/components/FreeModelsSection.tsx: append { id, labelKey } to PLATFORMS and mount the panel by id.
  2. Copy src/client/components/OpenRouterPanel.tsx as a starting point; ops carry platform: '<id>'.
  3. src/host/ops.ts: extend the runOp platform dispatch (fetch/filter per the platform's API, map its models onto RouteModelProfile, and teach model-config.ts the platform's credential ref and route naming).

Structure

├── src/host/*.ts      # Host half: /dsh-model-list/api route (platform-dispatched ops),
│                      # OpenRouter fetch+filter+sort (10 retries), cache store,
│                      # model-config writer (settings + credentials services)
├── src/client/*.tsx   # Browser half: settings.section shell with platform tabs,
│                      # per-platform panel (search, cards), add-to-model-list dialog
├── lib/index.js       # Host half build artifact (tsdown, ESM), committed
├── lib/client.js      # Browser half build artifact (tsdown → __ModuleLoader__ factory), committed
├── scripts/           # verify-client.mjs, test-ops.mts (offline), smoke-openrouter.mts (live)
├── tsdown.config.ts   # node half + client bundle banner wrapper
├── cordis.patch.yml   # Bundle patch: plugin row referenced by package name
└── package.json       # dsh.bundle + dsh.client(web) manifests + peerDependencies

Notes

  • OpenRouter free models still require an account token (daily rate limits apply); the dialog says so.
  • The write path is the sanctioned one: settings.update('llm-pi-ai', …) validates against the llm-pi-ai schema (a bad profile is refused where it is written) and the adapter re-registers routes live, so no host restart is needed after adding a model.
  • Manual edits to an existing OpenRouter route (custom apiKeyEnv, extra profile fields) are preserved: adding a model only rewrites that route's models array (merged by id, same id → replaced).
  • The plugin never touches settings/credentials files directly — everything goes through the host services; its own cache file (dsh-model-list.json) is the only file it writes.

Related plugins