npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cyrilmarin/dsh-lemonade

v0.4.0

Published

Lemonade Server (OpenAI-compatible) LLM provider plugin for the DeepSeek Harness

Readme

dsh-lemonade-provider

A dsh plugin that integrates Lemonade Server as a model provider for DeepSeek Harness.

Lemonade exposes an OpenAI-compatible API (Chat Completions). This plugin wires this API to the Harness's ctx.llm service under the lemonade provider route:

  • Chat Completions streaming (SSE) via POST {baseURL}/chat/completions
  • Model discovery live from GET {baseURL}/models
  • Optional API key (LEMONADE_API_KEY) — only needed when the server is configured with authentication
  • Vision support: Harness image blocks are sent as image_url (data URL) to vision models
  • Tool calling in standard OpenAI format

Prerequisites

  • A running Lemonade Server (default http://localhost:13305)
  • Node.js ≥ 22
  • A dsh installation (profile), e.g. the web profile

The package must be built beforehand: pnpm install && pnpm build ("Development" section below) — dsh loads code from lib/.

Installation in a profile

From the profile directory (e.g. ~/.dsh/profiles/web):

pnpm add file:../dsh-lemonade-provider

Or from npm (the published package is @cyrilmarin/dsh-lemonade):

pnpm add @cyrilmarin/dsh-lemonade@latest

Or, via the dsh command line:

dsh plugin --profile web add file:../dsh-lemonade-provider

Then add an entry to the profile's cordis.patch.yml (see example/cordis.patch.yml):

- id: llm-lemonade
  name: 'llm-lemonade'
  config:
    baseURL: http://localhost:13305

baseURL is the server root (an old /v1 suffix from legacy configs is also supported). If LEMONADE_BASE_URL is set, it is used when baseURL is omitted.

Configuration

| Field | Type | Default | Description | | --- | --- | --- | --- | | baseURL | string | http://localhost:13305 (or LEMONADE_BASE_URL) | Normalized to scheme://host/api/api appended if missing, /v1/… appended per endpoint | | apiKeyEnv | string (credential-ref) | LEMONADE_API_KEY | Regular API key (for /v1/* endpoints) | | adminApiKeyEnv | string (credential-ref) | LEMONADE_ADMIN_API_KEY | Optional admin API key (for internal /internal/* and /metrics endpoints) | | requireAuth | boolean | false | Fail when the key is absent (protected remote) | | models | array | [] | User-pinned advisory model catalog | | defaultContextWindow | number | 32768 | Context window used when the server doesn't declare one | | maxTokens | number | 8192 | Default output cap | | streamIdleTimeoutMs | number | 300000 | SSE stream idle timeout | | listingTimeoutMs | number | 5000 | Max time one live model-listing query may take (abort if exceeded) | | retryPolicy | object | default values | Provider retry policy |

Each entry in models: id (required), name, description, contextWindow, maxTokens, vision (boolean).

Model Discovery

The Harness Models page can query GET {baseURL}/models through the discovery registered for the llm-lemonade settings namespace. Undownloaded models and those routed to other endpoints (embeddings, image, TTS, transcription, …) are excluded from the proposed list; the declared context window (max_context_window) is reused when present.

UI Configuration (Settings → Models)

The dsh Settings/Models page has no third-party exit gate: its editor only knows the llm-deepseek and llm-pi-ai namespaces. The plugin therefore wires its edit card to the pi-ai card on the Models page through a targeted patch of the bundled dsh-client-ui-settings-models bundle (llm-lemonade route → pi-ai family). After any npm cache reinstallation (npm exec), rerun:

node scripts/patch-models-ui.mjs
node scripts/patch-models-ui-admin.mjs

The form (Settings → Models -> Lemonade row) allows entering the API key (optional, stored via the credentials service under LEMONADE_API_KEY), the optional admin key (LEMONADE_ADMIN_API_KEY — internal endpoints /internal/* and /metrics, via the patch-models-ui-admin.mjs patch), the base URL (falling back to "customized"), and selecting models served by Lemonade via "Fetch available models" (llm.discoverModels).

Lemonade View (Conversation Tab)

A Lemonade tab (next to Chat/Trajectory) exposes the entry points of the Lemonade-specific API (health/liveness, telemetry, models with Load/Unload/Delete/Files/Update/Info, LoRA adapter list/load/unload, controllable downloads, cloud keys, and a live log stream). The browser calls the dsh server on the same origin (/dsh-lemonade/api/<op>); the host proxy to Lemonade (src/server-api.ts) resolves baseURL + key (these never leave the host). Key selection is per endpoint: regular endpoints (/v1/*, /live) authenticate with LEMONADE_API_KEY, and control endpoints (/internal/*, /metrics) with LEMONADE_ADMIN_API_KEY (falling back to the regular key). The route is registered via ctx.webServer.register({ kind: 'prefix', path: '/dsh-lemonade/api', ... }) when the webServer service is available.

The model table supports batch operations: a select-all checkbox in the header and one per row let you mark models, then the Load selected / Unload selected / Delete selected buttons dispatch one call per selected model through the host proxy and report a done/total progress line plus how many operations failed. The batch delete prompts in a confirmation modal (instead of the browser's native confirm()) before it runs. A Logs pane (Logs button in the tab header) opens a live stream of the Lemonade server's own logs: the browser holds a plain SSE connection to the host proxy, which opens a WebSocket client to Lemonade's /logs/stream (the log port is discovered from GET /v1/healthwebsocket_port, which shares the Realtime Audio port) and re-emits every upstream log message as an SSE event.

Client browser bundle

The browser half lives in src/client/index.js and is copied verbatim to lib/client.js by the build (scripts/copy-client.mjs). The bundle registers itself with the module loader under the package name@cyrilmarin/dsh-lemonade — because the harness keys plugin client modules by package name in its boot manifest:

window.__ModuleLoader__.load({ id: "@cyrilmarin/dsh-lemonade", factory: (require) => { /* … */ } })

The registration id must match the graph row id exactly; a mismatch makes the harness fail with "loaded without registering <id> via __ModuleLoader__.load". Because lib/ is gitignored (build output), always rebuild after touching src/client/index.js and reinstall the package in the profile before reloading the GUI.

Development

pnpm install
pnpm build      # compile TypeScript to lib/
pnpm test       # protocol tests (simulated SSE server)

Structure

  • src/index.ts — plugin: config schema, apply, discovery, credentials
  • src/adapter.tsLemonadeAdapter extends LlmAdapter (fetch + SSE)
  • src/serialize.ts — Harness messages → OpenAI wire format
  • src/translate.ts — SSE payloads → StreamChunk chunks
  • src/client/index.js — browser half (Lemonade conversation tab), copied to lib/client.js
  • test/adapter.test.mjs — dependency-free test suite (mock HTTP)

License

MIT

Release

Release automation is a self-contained script with no third-party dependency: scripts/release.mjs. Available commands:

pnpm release:dry      # show the plan (no file writes, no mutating git commands)
pnpm release          # auto-detect the bump from the conventional commits
pnpm release:major    # force a major bump
pnpm release:minor    # force a minor bump
pnpm release:patch    # force a patch bump

The release script:

  1. Determines the increment from the conventional commits between the last tag (or the full history when no tag exists) and HEAD:

    | Commit | Bump | | --- | --- | | type(scope)!: … subject or BREAKING CHANGE: in the body | major | | feat | minor | | fix, perf | patch | | any other commit not yet tagged | patch (fallback) |

    With no commit to publish, it prints a message and exits 0 without doing anything.

  2. Bumps version in package.json (a prerelease suffix, if present, is dropped — the release is final).

  3. Commits chore(release): <version> (stages package.json only).

  4. Creates the annotated tag v<version> (prefix configurable via --tag-prefix).

  5. Pushes the current branch and the tag to origin (omitted with --no-push).

The npm publication itself is not performed by the script: it is triggered by the GitHub Release created on the pushed tag (workflow .github/workflows/publish.yml).