{"_id":"@cyrilmarin/dsh-lemonade","_rev":"3-b2701307035d1ee5905fcc0a2a3f963f","name":"@cyrilmarin/dsh-lemonade","dist-tags":{"latest":"0.4.0"},"versions":{"0.2.1":{"name":"@cyrilmarin/dsh-lemonade","version":"0.2.1","license":"MIT","_id":"@cyrilmarin/dsh-lemonade@0.2.1","maintainers":[{"name":"ziouf","email":"marin.cyril@gmail.com"}],"dsh":{"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-api-remotes"],"platform":"web"}},"dist":{"shasum":"2260f7100e0be29913a3b87a60da88a5ed49d426","tarball":"https://registry.npmjs.org/@cyrilmarin/dsh-lemonade/-/dsh-lemonade-0.2.1.tgz","fileCount":20,"integrity":"sha512-aWqHrrB7/sJgnS/ZFhhIS8fiW+46lqS6GsyabRBrrle3EvhLFjA2MeIg5wXdWsMSyKTPTmT/B6xf6BH2dLUNhw==","signatures":[{"sig":"MEQCIEGoJJBGbyFzbcduCIc4c2mPvYxYRBABDPqcy7WoOWAUAiAjtqugG0IC10/QVZba1avnjoo8p6FmUBRHflCqopWUdQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":214646},"main":"lib/index.js","type":"module","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./client":{"default":"./lib/client.js"},"./package.json":"./package.json"},"scripts":{"test":"node test/adapter.test.mjs && node test/server-api.test.mjs && node test/client-bundle.test.mjs lib/client.js","build":"tsc -p tsconfig.json && node scripts/copy-client.mjs","release":"node scripts/release.mjs","typecheck":"tsc -p tsconfig.json --noEmit","release:dry":"node scripts/release.mjs --dry-run","release:major":"node scripts/release.mjs --bump major","release:minor":"node scripts/release.mjs --bump minor","release:patch":"node scripts/release.mjs --bump patch"},"_npmUser":{"name":"ziouf","email":"marin.cyril@gmail.com"},"description":"Lemonade Server (OpenAI-compatible) LLM provider plugin for the DeepSeek Harness","directories":{},"_nodeVersion":"24.18.0","dependencies":{"eventsource-parser":"^3.1.0","@deepseek-ai/schemastery":"^3.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","typescript":"^5.5.0","@types/node":"^24.0.0","@types/react":"~18.3.1","eventsource-parser":"^3.1.0","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.7","@deepseek-ai/dsh-timeout":"^0.1.0-rc.7","@deepseek-ai/schemastery":"^3.18.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-attachment":"^0.1.0-rc.7","@deepseek-ai/dsh-api-remotes":"^0.1.0-rc.7","@deepseek-ai/dsh-credentials":"^0.1.0-rc.7","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.7","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.7"},"peerDependencies":{"react":"^18.2.0","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.7","@deepseek-ai/dsh-timeout":"^0.1.0-rc.7","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-attachment":"^0.1.0-rc.7","@deepseek-ai/dsh-api-remotes":"^0.1.0-rc.7","@deepseek-ai/dsh-credentials":"^0.1.0-rc.7","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.7","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.7"},"_npmOperationalInternal":{"tmp":"tmp/dsh-lemonade_0.2.1_1787159475416_0.7798171877156808","host":"s3://npm-registry-packages-npm-production"}},"0.2.6":{"name":"@cyrilmarin/dsh-lemonade","version":"0.2.6","license":"MIT","_id":"@cyrilmarin/dsh-lemonade@0.2.6","maintainers":[{"name":"ziouf","email":"marin.cyril@gmail.com"}],"homepage":"https://github.com/ziouf/dsh-lemonade-provider#readme","bugs":{"url":"https://github.com/ziouf/dsh-lemonade-provider/issues"},"dsh":{"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-api-remotes"],"platform":"web"}},"dist":{"shasum":"da29f5cc5e038926b2979f836606892346f40c93","tarball":"https://registry.npmjs.org/@cyrilmarin/dsh-lemonade/-/dsh-lemonade-0.2.6.tgz","fileCount":20,"integrity":"sha512-a4aEMx9eSCpBvh2Afjp/dqtrAjZtdWcziUdJrT4MrLzisnzMT81rdG+RPwUuwB8kSB79QMnojhfpRXNvFN8FUg==","signatures":[{"sig":"MEUCIEVmJCzIFCc8LtjpKsvCXOOAs9ncEwxZgIFtcJvaQkO7AiEAvz+8v2UTOTDrOgLdvVugEMbSQkLQe8fYXjMIoQTZfVw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyrilmarin%2fdsh-lemonade@0.2.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":215382},"main":"lib/index.js","type":"module","_from":"file:cyrilmarin-dsh-lemonade-0.2.6.tgz","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./client":{"default":"./lib/client.js"},"./package.json":"./package.json"},"scripts":{"test":"node test/adapter.test.mjs && node test/server-api.test.mjs && node test/client-bundle.test.mjs lib/client.js","build":"tsc -p tsconfig.json && node scripts/copy-client.mjs","release":"node scripts/release.mjs","typecheck":"tsc -p tsconfig.json --noEmit","release:dry":"node scripts/release.mjs --dry-run","release:major":"node scripts/release.mjs --bump major","release:minor":"node scripts/release.mjs --bump minor","release:patch":"node scripts/release.mjs --bump patch"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:eced6d19-4843-4dce-84b9-163314a7b2f5"}},"_resolved":"/tmp/37e43d8a77c76e9fd26aacc6326b0b46/cyrilmarin-dsh-lemonade-0.2.6.tgz","_integrity":"sha512-a4aEMx9eSCpBvh2Afjp/dqtrAjZtdWcziUdJrT4MrLzisnzMT81rdG+RPwUuwB8kSB79QMnojhfpRXNvFN8FUg==","repository":{"url":"git+https://github.com/ziouf/dsh-lemonade-provider.git","type":"git"},"_npmVersion":"11.17.0","description":"Lemonade Server (OpenAI-compatible) LLM provider plugin for the DeepSeek Harness","directories":{},"_nodeVersion":"24.19.0","dependencies":{"eventsource-parser":"^3.1.0","@deepseek-ai/schemastery":"^3.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^18.2.0","typescript":"^5.5.0","@types/node":"^24.0.0","@types/react":"~18.3.1","eventsource-parser":"^3.1.0","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.7","@deepseek-ai/dsh-timeout":"^0.1.0-rc.7","@deepseek-ai/schemastery":"^3.18.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-attachment":"^0.1.0-rc.7","@deepseek-ai/dsh-api-remotes":"^0.1.0-rc.7","@deepseek-ai/dsh-credentials":"^0.1.0-rc.7","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.7","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.7"},"peerDependencies":{"react":"^18.2.0","@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"^0.1.0-rc.7","@deepseek-ai/dsh-timeout":"^0.1.0-rc.7","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-attachment":"^0.1.0-rc.7","@deepseek-ai/dsh-api-remotes":"^0.1.0-rc.7","@deepseek-ai/dsh-credentials":"^0.1.0-rc.7","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.7","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.7"},"_npmOperationalInternal":{"tmp":"tmp/dsh-lemonade_0.2.6_1787172670572_0.641687240277026","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@cyrilmarin/dsh-lemonade","description":"Lemonade Server (OpenAI-compatible) LLM provider plugin for the DeepSeek Harness","version":"0.4.0","type":"module","main":"lib/index.js","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./package.json":"./package.json","./client":{"default":"./lib/client.js"}},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/ziouf/dsh-lemonade-provider.git"},"publishConfig":{"access":"public"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-attachment":"^0.1.0-rc.7","@deepseek-ai/dsh-credentials":"^0.1.0-rc.7","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.7","@deepseek-ai/dsh-llm":"^0.1.0-rc.7","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-timeout":"^0.1.0-rc.7","react":"^18.2.0","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.7","@deepseek-ai/dsh-api-remotes":"^0.1.0-rc.7"},"dependencies":{"@deepseek-ai/schemastery":"^3.18.1","eventsource-parser":"^3.1.0"},"devDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-attachment":"^0.1.0-rc.7","@deepseek-ai/dsh-credentials":"^0.1.0-rc.7","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.7","@deepseek-ai/dsh-llm":"^0.1.0-rc.7","@deepseek-ai/dsh-settings":"^0.1.0-rc.7","@deepseek-ai/dsh-timeout":"^0.1.0-rc.7","@deepseek-ai/schemastery":"^3.18.1","@types/node":"^24.0.0","eventsource-parser":"^3.1.0","typescript":"^5.5.0","react":"^18.2.0","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.7","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.7","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.7","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.7","@deepseek-ai/dsh-api-remotes":"^0.1.0-rc.7","@types/react":"~18.3.1"},"dsh":{"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-api-remotes"],"platform":"web"}},"scripts":{"build":"tsc -p tsconfig.json && node scripts/copy-client.mjs","typecheck":"tsc -p tsconfig.json --noEmit","test":"node test/adapter.test.mjs && node test/server-api.test.mjs && node test/json-parse.test.mjs && node test/translate.test.mjs && node test/client-bundle.test.mjs lib/client.js","release":"node scripts/release.mjs","release:dry":"node scripts/release.mjs --dry-run","release:major":"node scripts/release.mjs --bump major","release:minor":"node scripts/release.mjs --bump minor","release:patch":"node scripts/release.mjs --bump patch"},"_id":"@cyrilmarin/dsh-lemonade@0.4.0","bugs":{"url":"https://github.com/ziouf/dsh-lemonade-provider/issues"},"homepage":"https://github.com/ziouf/dsh-lemonade-provider#readme","_integrity":"sha512-WZIYjZoQvl98XI/Ub+MzAIkNOKWnm3DU6dfw5tHIQjZYOUDC7ZJMzSk2rwTxmYMIk82bVCCDAwK5aF3jmIA5lw==","_resolved":"/tmp/4007b34b995a0e3e1b74d6d6909ed6cf/cyrilmarin-dsh-lemonade-0.4.0.tgz","_from":"file:cyrilmarin-dsh-lemonade-0.4.0.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-WZIYjZoQvl98XI/Ub+MzAIkNOKWnm3DU6dfw5tHIQjZYOUDC7ZJMzSk2rwTxmYMIk82bVCCDAwK5aF3jmIA5lw==","shasum":"cc429cbd07c0ee89846807669bba76275c72d578","tarball":"https://registry.npmjs.org/@cyrilmarin/dsh-lemonade/-/dsh-lemonade-0.4.0.tgz","fileCount":23,"unpackedSize":286292,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyrilmarin%2fdsh-lemonade@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDO2PStkeVUNt0J87/Dvf/x/HluMpxWBl7TO16ByrR6IgIgbNwCDqRSH1zP74RJrq7V4RC9LNu2AQ2TPeVPoj2Vrf0="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:eced6d19-4843-4dce-84b9-163314a7b2f5"}},"directories":{},"maintainers":[{"name":"ziouf","email":"marin.cyril@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-lemonade_0.4.0_1787382537096_0.3444549973315454"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-19T17:11:14.525Z","modified":"2026-08-22T07:08:57.531Z","0.2.1":"2026-08-19T17:11:15.585Z","0.2.6":"2026-08-19T20:51:10.720Z","0.4.0":"2026-08-22T07:08:57.234Z"},"bugs":{"url":"https://github.com/ziouf/dsh-lemonade-provider/issues"},"license":"MIT","homepage":"https://github.com/ziouf/dsh-lemonade-provider#readme","repository":{"type":"git","url":"git+https://github.com/ziouf/dsh-lemonade-provider.git"},"description":"Lemonade Server (OpenAI-compatible) LLM provider plugin for the DeepSeek Harness","maintainers":[{"name":"ziouf","email":"marin.cyril@gmail.com"}],"readme":"# dsh-lemonade-provider\n\nA **dsh** plugin that integrates [Lemonade Server](https://lemonade-server.ai) as\na model provider for DeepSeek Harness.\n\nLemonade exposes an OpenAI-compatible API (Chat Completions). This plugin wires\nthis API to the Harness's `ctx.llm` service under the **`lemonade`** provider\nroute:\n\n- **Chat Completions** streaming (SSE) via `POST {baseURL}/chat/completions`\n- **Model discovery** live from `GET {baseURL}/models`\n- **Optional API key** (`LEMONADE_API_KEY`) — only needed when\n  the server is configured with authentication\n- **Vision support**: Harness image blocks are sent as `image_url`\n  (data URL) to `vision` models\n- **Tool calling** in standard OpenAI format\n\n## Prerequisites\n\n- A running Lemonade Server (default `http://localhost:13305`)\n- Node.js ≥ 22\n- A dsh installation (profile), e.g. the `web` profile\n\n> The package must be **built beforehand**: `pnpm install && pnpm build`\n> (\"Development\" section below) — dsh loads code from `lib/`.\n\n## Installation in a profile\n\nFrom the profile directory (e.g. `~/.dsh/profiles/web`):\n\n```sh\npnpm add file:../dsh-lemonade-provider\n```\n\nOr from npm (the published package is `@cyrilmarin/dsh-lemonade`):\n\n```sh\npnpm add @cyrilmarin/dsh-lemonade@latest\n```\n\nOr, via the dsh command line:\n\n```sh\ndsh plugin --profile web add file:../dsh-lemonade-provider\n```\n\nThen add an entry to the profile's `cordis.patch.yml` (see\n[example/cordis.patch.yml](example/cordis.patch.yml)):\n\n```yaml\n- id: llm-lemonade\n  name: 'llm-lemonade'\n  config:\n    baseURL: http://localhost:13305\n```\n\n> `baseURL` is the server root (an old `/v1` suffix from legacy configs is also\n> supported). If `LEMONADE_BASE_URL` is set, it is used when `baseURL` is omitted.\n\n## Configuration\n\n| Field | Type | Default | Description |\n| --- | --- | --- | --- |\n| `baseURL` | string | `http://localhost:13305` (or `LEMONADE_BASE_URL`) | Normalized to `scheme://host/api` — `/api` appended if missing, `/v1/…` appended per endpoint |\n| `apiKeyEnv` | string (credential-ref) | `LEMONADE_API_KEY` | Regular API key (for /v1/* endpoints) |\n| `adminApiKeyEnv` | string (credential-ref) | `LEMONADE_ADMIN_API_KEY` | Optional admin API key (for internal /internal/* and /metrics endpoints) |\n| `requireAuth` | boolean | `false` | Fail when the key is absent (protected remote) |\n| `models` | array | `[]` | User-pinned advisory model catalog |\n| `defaultContextWindow` | number | `32768` | Context window used when the server doesn't declare one |\n| `maxTokens` | number | `8192` | Default output cap |\n| `streamIdleTimeoutMs` | number | `300000` | SSE stream idle timeout |\n| `listingTimeoutMs` | number | `5000` | Max time one live model-listing query may take (abort if exceeded) |\n| `retryPolicy` | object | default values | Provider retry policy |\n\nEach entry in `models`: `id` (required), `name`, `description`,\n`contextWindow`, `maxTokens`, `vision` (boolean).\n\n## Model Discovery\n\nThe Harness Models page can query `GET {baseURL}/models` through the\ndiscovery registered for the `llm-lemonade` settings namespace. Undownloaded\nmodels and those routed to other endpoints (embeddings, image,\nTTS, transcription, …) are excluded from the proposed list; the declared\ncontext window (`max_context_window`) is reused when present.\n\n## UI Configuration (Settings → Models)\n\nThe dsh Settings/Models page has no third-party exit gate: its editor only\nknows the `llm-deepseek` and `llm-pi-ai` namespaces. The plugin therefore wires\nits edit card to the `pi-ai` card on the Models page through a targeted patch\nof the bundled `dsh-client-ui-settings-models` bundle (`llm-lemonade` route →\n`pi-ai` family). After any npm cache reinstallation (`npm exec`), rerun:\n\n```sh\nnode scripts/patch-models-ui.mjs\nnode scripts/patch-models-ui-admin.mjs\n```\n\nThe form (Settings → Models -> Lemonade row) allows entering the API key\n(optional, stored via the credentials service under `LEMONADE_API_KEY`),\nthe **optional admin key** (`LEMONADE_ADMIN_API_KEY` — internal endpoints\n`/internal/*` and `/metrics`, via the `patch-models-ui-admin.mjs` patch),\nthe base URL (falling back to \"customized\"), and selecting models served by\nLemonade via \"Fetch available models\" (`llm.discoverModels`).\n\n## Lemonade View (Conversation Tab)\n\nA **Lemonade** tab (next to Chat/Trajectory) exposes the entry points of the\nLemonade-specific API (health/liveness, telemetry, models with\nLoad/Unload/Delete/Files/Update/Info, LoRA adapter list/load/unload,\ncontrollable downloads, cloud keys, and a live log stream).\nThe browser calls the dsh server on the same origin (`/dsh-lemonade/api/<op>`);\nthe host proxy to Lemonade (`src/server-api.ts`) resolves baseURL + key\n(these never leave the host). Key selection is **per endpoint**: regular\nendpoints (/v1/*, /live) authenticate with `LEMONADE_API_KEY`, and control\nendpoints (`/internal/*`, `/metrics`) with `LEMONADE_ADMIN_API_KEY` (falling\nback to the regular key). The route is registered via\n`ctx.webServer.register({ kind: 'prefix', path: '/dsh-lemonade/api', ... })`\nwhen the `webServer` service is available.\n\nThe model table supports **batch operations**: a select-all checkbox in the\nheader and one per row let you mark models, then the **Load selected** /\n**Unload selected** / **Delete selected** buttons dispatch one call per selected\nmodel through the host proxy and report a `done/total` progress line plus how\nmany operations failed. The batch delete prompts in a **confirmation modal**\n(instead of the browser's native `confirm()`) before it runs. A **Logs** pane\n(**Logs button in the tab header**) opens a live stream of the Lemonade server's\nown logs: the browser holds a plain SSE connection to the host proxy, which opens\na WebSocket *client* to Lemonade's `/logs/stream` (the log port is discovered\nfrom `GET /v1/health` → `websocket_port`, which shares the Realtime Audio port)\nand re-emits every upstream log message as an SSE event.\n\n### Client browser bundle\n\nThe browser half lives in `src/client/index.js` and is copied verbatim to\n`lib/client.js` by the build (`scripts/copy-client.mjs`). The bundle registers\nitself with the module loader under the **package name** — `@cyrilmarin/dsh-lemonade`\n— because the harness keys plugin client modules by package name in its boot\nmanifest:\n\n```js\nwindow.__ModuleLoader__.load({ id: \"@cyrilmarin/dsh-lemonade\", factory: (require) => { /* … */ } })\n```\n\nThe registration id must match the graph row id exactly; a mismatch makes the\nharness fail with *\"loaded without registering `<id>` via `__ModuleLoader__.load`\"*.\nBecause `lib/` is gitignored (build output), always rebuild after touching\n`src/client/index.js` and reinstall the package in the profile before reloading\nthe GUI.\n\n## Development\n\n```sh\npnpm install\npnpm build      # compile TypeScript to lib/\npnpm test       # protocol tests (simulated SSE server)\n```\n\n### Structure\n\n- `src/index.ts` — plugin: config schema, `apply`, discovery, credentials\n- `src/adapter.ts` — `LemonadeAdapter extends LlmAdapter` (fetch + SSE)\n- `src/serialize.ts` — Harness messages → OpenAI wire format\n- `src/translate.ts` — SSE payloads → `StreamChunk` chunks\n- `src/client/index.js` — browser half (Lemonade conversation tab), copied to `lib/client.js`\n- `test/adapter.test.mjs` — dependency-free test suite (mock HTTP)\n\n### License\n\nMIT\n\n## Release\n\nRelease automation is a self-contained script with no third-party dependency:\n`scripts/release.mjs`. Available commands:\n\n```sh\npnpm release:dry      # show the plan (no file writes, no mutating git commands)\npnpm release          # auto-detect the bump from the conventional commits\npnpm release:major    # force a major bump\npnpm release:minor    # force a minor bump\npnpm release:patch    # force a patch bump\n```\n\nThe release script:\n\n1. Determines the increment from the conventional commits between the last\n   tag (or the full history when no tag exists) and HEAD:\n\n   | Commit | Bump |\n   | --- | --- |\n   | `type(scope)!: …` subject or `BREAKING CHANGE:` in the body | major |\n   | `feat` | minor |\n   | `fix`, `perf` | patch |\n   | any other commit not yet tagged | patch (fallback) |\n\n   With no commit to publish, it prints a message and exits 0 without doing\n   anything.\n2. Bumps `version` in `package.json` (a prerelease suffix, if present, is\n   dropped — the release is final).\n3. Commits `chore(release): <version>` (stages `package.json` only).\n4. Creates the annotated tag `v<version>` (prefix configurable via\n   `--tag-prefix`).\n5. Pushes the current branch and the tag to `origin` (omitted with\n   `--no-push`).\n\n> The npm publication itself is not performed by the script: it is triggered\n> by the GitHub Release created on the pushed tag (workflow\n> [.github/workflows/publish.yml](.github/workflows/publish.yml)).\n","readmeFilename":"README.md"}