DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-openrouter-models

t

dsh-openrouter-models

Manifest valid

OpenRouter model catalog and multi-key router for DeepSeek Harness — live catalog, price filters, and one llm-pi-ai route per API key.

UI (client)hasBundlePatch

dsh-openrouter-models

English · 简体中文

CI License: MIT Node

An OpenRouter model catalog and multi-key router for DeepSeek Harness. It adds one settings page where you can:

  • browse OpenRouter's live model catalog (hundreds of models, changing with the upstream), search by name / id / vendor, filter by input or output price, toggle free only, and sort by price or context length;
  • keep several OpenRouter API keys as named "accounts", each bound to its own subset of models;
  • get one native model route per account (openrouter-<account>), so each key shows up separately in the model picker and in /model.

The plugin is deliberately self-contained: no runtime dependencies, no build step, and no imports from @deepseek-ai/*. The whole thing is the six files in lib/.

Why another OpenRouter plugin

OpenRouter speaks the OpenAI Chat Completions protocol, which Harness's llm-pi-ai service already drives. So instead of shipping a private streaming adapter, this plugin declares one ordinary llm-pi-ai route per key. You get the native picker, default-model selection, retries, replay handling, and credential resolution for free — and the plugin never handles token streams or speaks to the network on a request path.

Because a route resolves its key from its own credential reference, separate routes are exactly the host's mechanism for several keys against one provider.

Install

Requires DeepSeek Harness 0.1.5-rc.2 or newer and Node.js 20+.

Desktop app — open Settings → Plugins and install dsh-openrouter-models once it is on npm, or quit the app completely (tray included) and run:

# from npm (once the package is published)
dsh plugin --profile desktop add dsh-openrouter-models

# or straight from GitHub — no npm release needed
dsh plugin --profile desktop add github:type53/dsh-openrouter-models

# or from a local checkout you are editing
git clone https://github.com/type53/dsh-openrouter-models.git
dsh plugin --profile desktop add link:/absolute/path/to/dsh-openrouter-models

The CLI refuses to touch the desktop profile while the app is running, so quit it first. For a CLI-only profile (web, headless, or a custom name), use that name instead of desktop.

Restart DSH after installing — the host half mounts only at startup. Then open Settings → OpenRouter 模型 (the panel is Chinese today; see Internationalization).

Usage

  1. Create an account and name it (for example main or backup). The account id is derived from the name and becomes part of the route, so an ASCII name works best; a purely non-ASCII name falls back to account.
  2. Paste the key and save. It is written only to the harness credential store as OPENROUTER_KEY_<ACCOUNT>; it never enters the plugin's JSON file and is never sent back to the page. Press Verify key to check quota.
  3. Pick models from the catalog: select an account, then click models to add or remove them. Filter by name, by an input/output price range in $/M tokens, or with free only; sort by price, context, or name.
  4. Use them from the model picker at the bottom of a conversation, under OpenRouter · <account name>.

A key alone produces no route: llm-pi-ai rejects a hand-declared route with an empty model list. Bind at least one model to an account for it to appear in the picker.

How it maps to DSH

One account becomes one hand-declared llm-pi-ai provider:

llm-pi-ai:
  providers:
    openrouter-main:                    # account id = main
      displayName: OpenRouter · Main
      apiKeyEnv: OPENROUTER_KEY_MAIN    # credential reference, not the key
      api: openai-completions
      baseURL: https://openrouter.ai/api/v1
      compat: { supportsDeveloperRole: false, thinkingFormat: openrouter }
      models: [ ... the models you picked ... ]
  • The route is written into the profile's cordis.patch.yml (the user settings layer), so it takes effect on the next request without a restart. On every boot the plugin re-syncs it from the account file.
  • contextWindow, maxTokens, input modalities, and reasoningEfforts are filled from the live catalog when the route is derived. A copy is stored when you pick a model, so a route built while the catalog is unreachable still declares reasoning support correctly.
  • Removing an account or clearing its models removes only the route this plugin owns; other llm-pi-ai providers are untouched.

Data and security

DataLocation
Accounts and their selected models$DSH_HOME/openrouter-models.json (default ~/.dsh/openrouter-models.json), written atomically, safe to inspect, back up, or delete
OpenRouter API keysThe harness credential store, reference OPENROUTER_KEY_<ACCOUNT>
Derived routesThe profile's cordis.patch.yml
  • Two outbound destinations, both on openrouter.ai: /api/v1/models (public catalog, no credentials attached) and /api/v1/key (only when you press Verify key, with that account's key). No telemetry, no third-party reporting.
  • All host routes are registered on Connection's authenticated /api channel, so they inherit Host/Origin checks and browser-session authentication. A request from an unauthenticated client is rejected with 401; plain webServer routes would not have that fence.
  • Keys are write-only: the state route reports only whether a key is configured, and verification returns only quota metadata.

See SECURITY.md for the disclosure process.

Development

No pnpm install is needed — the tests run on a bare checkout:

node scripts/unit-test.mjs        # pure logic: naming, validation, prices, catalog, route derivation, store
node scripts/host-boot-test.mjs   # host half: stub seams + all five HTTP routes
node scripts/client-boot-test.mjs # browser half: slot registration, filter/sort, render smoke test

npm test runs all three. CI runs them on Node 20, 22, and 24.

Source map:

FileResponsibility
lib/index.jsHost half: account → llm-pi-ai route reconciliation, the five HTTP routes
lib/config.jsConstants, id / credential naming, account validation
lib/store.jsAtomic read/write of the account document
lib/catalog.jsCatalog fetch, normalization, price arithmetic
lib/routes.jsPure account → llm-pi-ai route mapping
lib/client.jsSettings page UI (hand-written __ModuleLoader__ bundle, no build)

Client-only edits in lib/client.js apply on a page refresh: the bundle URL is content-hashed. Host edits need a DSH restart.

Layout note

The settings page is hosted by the platform's settings dialog, which is a fixed 800px panel (188px nav, 24px side padding), leaving roughly 564px of usable width — less in a small window, and a plugin cannot widen it (that CSS belongs to the platform). The layout is designed for that constraint:

  • no fixed control widths; inputs fill their grid cell;
  • buttons and inputs share one 28px height and one font size, so rows align;
  • account settings use a label | control grid with a shared left edge;
  • filters are a caption-over-control grid that wraps to about four columns;
  • accounts stack vertically, one per row;
  • model names and ids wrap (overflow-wrap: anywhere) instead of truncating — OpenRouter ids are long, and the id is what a request actually needs.

Known limitations

  • Filters are not persisted; they reset when you leave the page. Accounts and model selections are persisted.
  • The filtered list is capped at 600 rows to avoid rendering ~500 items at once.
  • Variable-priced models (openrouter/auto and a few other routers) report -1 upstream, meaning "depends on the route chosen". They display as variable, sort last, and match no price range.
  • All models use one protocol: openai-completions. OpenRouter serves that protocol for every model on /api/v1, so this does not reduce availability.
  • Deleting an account, disabling it, or clearing its models removes its openrouter-<id> route (only that one).
  • Account ids must match [a-z][a-z0-9-]* because they become route names and credential references.
  • A key with no models bound produces no route (see Usage).

Internationalization

The settings page currently ships Chinese strings only, matching the Harness UI this plugin was built against. Extracting strings into a locale layer is a welcome contribution; see CONTRIBUTING.md.

License

MIT © 2026 type53

This project is not affiliated with OpenRouter or DeepSeek. OpenRouter and DeepSeek Harness are the property of their respective owners.

Comments

Loading…

Similar plugins

dsh-openrouter-live

OpenRouter provider for DeepSeek Harness with a live model list, prices and spend tracking

Terminal & ClientsModels & ProvidersManifest valid

★ 0

MIT

JavaScript

dsh plugin --profile web add dsh-openrouter-live

by icyaaaww

Deterministic per-turn adaptive model routing for DeepSeek Harness

Models & ProvidersManifest valid

★ 0

MIT

JavaScript

Aug 24, 2026

dsh plugin --profile web add dsh-adaptive-model-router

by pigman18

Exposes every model of every provider configured in DeepSeek Harness through one OpenAI-compatible endpoint on a fixed loopback port, with a sidebar page for the base URL, key, port and model ids.

Models & ProvidersManifest valid

★ 0

MIT

JavaScript

Oct 23, 2026

dsh plugin --profile web add dsh-openai-gateway

by scavanger2221

OpenCode Go models as a DeepSeek Harness provider, with session-affinity headers, a live model catalog, and a Web settings card

Manifest valid

★ 0

MIT

JavaScript

Sep 29, 2026

dsh plugin --profile web add dsh-llm-opencode-go

by WeatherWind

Real-time model-list refresh for DeepSeek Harness llm-pi-ai provider routes: query the live endpoint, merge the installed pi-ai catalog metadata onto new ids, append-only writes through the official settings seam.

Tools & CapabilitiesManifest valid

★ 0

MIT

TypeScript

Sep 29, 2026

dsh plugin --profile web add dsh-llm-pi-ai-live

OpenCode Zen free models for DeepSeek Harness, with live catalog discovery and no dependencies.

Models & ProvidersManifest valid

★ 0

MIT

JavaScript

dsh plugin --profile web add dsh-zen-adapter