DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Plugin Openai Api — DSH Plugin for DeepSeek Harness
← Plugins
P

dsh-plugin-openai-api

Plugin Openai Api

OpenAI-compatible /v1/chat/completions (streaming and non-streaming) and /v1/models endpoint on the DSH web server: one DSH agent session per request user field.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:paulalesius/dsh-openai-api#ddc859fa3e2d1fa72c39b6fac47ea056a6b0fbbc
READMECompatibilityVersions

Compatibility and provenance

Plugin Openai Api is published as dsh-plugin-openai-api and currently resolves to version 0.1.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
github
Registry updated
8/24/2026

Versions

0.1.0stable
8/24/2026

Related plugins

Loading related plugins…

Latest
0.1.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
any
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
8/24/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in integrations-communication.

Acp App@deepseek-ai/dsh-acp-appThe dsh ACP profile bundle: automation-only JSON-RPC stdio and process lifecycle over dsh-baseRemote Web Ui@linxin666/dsh-remote-web-uiScan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and rIm@xmanrui/dsh-im把十一种 IM 渠道和公网 AI Office 接入本机 DeepSeek Harness。 Connect eleven IM channels and a public AI Office to a local DeepSeek Harness.Pocketdsh-pocketPut DeepSeek Harness in your pocket: one package, one settings page, and scan a QR code on your phone to access DSH on your computer in sync (LAN + public network, real-time screen mirroring).

README

dsh-plugin-openai-api

dsh-plugin-openai-api banner

An OpenAI-compatible endpoint on your own DeepSeek Harness (dsh) web server. Point any OpenAI SDK client at the running dsh web instance and it drives a real DSH agent session — with all the tools and presets that agent has:

GET  /v1/models
POST /v1/chat/completions        (non-streaming and SSE streaming)

Base URL of a default local install: http://127.0.0.1:3080/v1 — the same port the web GUI serves on.

What the endpoint gives you:

  • a real agent on the other end — every request lands on a DSH agent session, not a thin LLM proxy: tool calls, reasoning, and the full session machinery happen server-side, and only committed assistant text comes back over the wire;
  • agent presets as clients — the request's user field names the agent preset the session joins (standard, or anything in your ~/.dsh/.agent-presets/); one preset = one session, by design;
  • two session lifetimes — shared (the default: one session per user, conversation state survives between requests) or per_request (a fresh session seeded from the full message array on every prompt);
  • standard OpenAI surface — streaming with usage, the standard error envelope, chatcmpl-… ids, and the finish_reason mapping, so SDK code you already have works unchanged.

The plugin is host-plane: it mounts on the web server at startup, and installing it never requires rebuilding the frontend.

Installation

You need a running dsh web with a profile that includes the web-app bundle (the one that owns the port).

With the dsh command

1. Install the plugin. One command does both halves — it links the plugin into your profile and registers the mounting row for it:

dsh plugin --profile web add /path/to/openai-api

(Use a plain path, not a file: URL — a plain path links the source, so edits are picked up without reinstalling. Without the dsh binary on your PATH, run node --import tsx/esm apps/cli/src/bin.ts plugin … from inside the DSH checkout.)

If you checked this repo out fresh, recreate the one machine-local link the source needs first: ln -s /path/to/deepseek-harness/apps/cli/node_modules node_modules inside this repo.

2. Restart dsh web once.

3. Enable and configure the endpoint — one small block in your own config, below. Then restart dsh web again, and the endpoint is live.

The install ships switched off on purpose: until you add that block, the port serves only the GUI. What you enable, with what key, is your call.

From source (no dsh command)

The command above does exactly these three things, by hand:

  1. Link the package into the profile — in ~/.dsh/profiles/web/package.json, add the dependency (alongside the existing ones) and the bundle (into the existing list):

    {
      "dependencies": {
        "dsh-plugin-openai-api": "link:/home/noname/deepseek-harness/dsh-plugins/openai-api"
      },
      "dsh": {
        "profile": {
          "bundles": [
            "…",
            "dsh-plugin-openai-api"
          ]
        }
      }
    }
    
  2. Recreate the machine-local source link (fresh checkouts only): ln -s /path/to/deepseek-harness/apps/cli/node_modules node_modules inside this repo — the plugin's TypeScript resolves the DSH packages through it.

  3. Enable the row in the profile patch layer (next section).

Then restart dsh web.

To uninstall later — command path: dsh plugin --profile web remove dsh-plugin-openai-api. Source path: remove the two lines above from the profile's package.json. Either way, remove your config block and restart dsh web. Sessions the plugin created are disposed with it; the web GUI's own sessions are untouched.

Configuration

The configuration is one openai-api row in your profile's patch layer (~/.dsh/profiles/web/cordis.patch.yml, append to your existing file):

- id: openai-api
  disabled: false
  config:
    model: default
    apiKey: local-key

Two things to know about the row:

  • the config block replaces the plugin's defaults wholesale — list every key you want; nothing is required, and unknown keys are a load error;
  • to switch the endpoint off again, set disabled: true on the row (or remove it) and restart.

The configuration keys

All four are optional.

keydefaultwhat it does
modeldefaultThe model id the endpoint advertises at /v1/models and echoes in completions. It is the wire identity — what SDKs see — not a DSH model selector: a request that omits model (or names the wire id) runs the host's effective default model selection — the same selection GUI sessions get, from the agent-default-model service, which honors the user's stored model choice. Any other value in a request is a real model id, passed through to the session. (The deployment persona renders {{model}} from the session's model, so every session always carries one.)
providerhost defaultThe provider route the created sessions use (the AgentOptions.provider meaning — the host's provider routing). Omitting it is a no-op on single-provider setups.
apiKeyunset (no auth)When set, every request must carry Authorization: Bearer <apiKey> — missing or wrong key is a 401. When unset the endpoint is unauthenticated: anyone who can reach the port can prompt the agent (which has the same tool access as the GUI). Keep the web server on its loopback bind (host: 127.0.0.1) unless you set this — and consider a reverse proxy — before exposing the port.
cwdthe dsh web process's own working directoryAbsolute working directory for the sessions the plugin creates (they run real file and shell tools, so they have a working directory — and the deployment persona in the system prompt renders it, so every session always carries one). Must start with /.

Using the API

The contract is OpenAI's, with two DSH-specific fields (user, session) and one honest limitation: only committed assistant text crosses the wire.

A request

import OpenAI from 'openai'

const client = new OpenAI({
  baseURL: 'http://127.0.0.1:3080/v1',
  apiKey: 'local-key', // the config's apiKey; 'unused' if you set none
})

const reply = await client.chat.completions.create({
  model: 'default',
  user: 'webtroll', // agent preset id; omit for the default preset
  messages: [
    { role: 'user', content: 'What are you? ' },
    { role: 'assistant', content: "I'm a DSH agent." },
    { role: 'user', content: 'Write a haiku about that.' },
  ],
})
console.log(reply.choices[0].message.content)

// streaming, same session (`user: 'webtroll'` matches the first request's
// session), with the trailing usage chunk
const stream = await client.chat.completions.create({
  model: 'default',
  user: 'webtroll',
  stream: true,
  stream_options: { include_usage: true },
  messages: [{ role: 'user', content: 'Count to five, slowly.' }],
})
for await (const chunk of stream) {
  if (chunk.choices[0]?.delta?.content) process.stdout.write(chunk.choices[0].delta.content)
}

// a fresh session for every prompt, seeded from the full message array
const once = await client.chat.completions.create({
  model: 'default',
  session: 'per_request',
  messages: [
    { role: 'system', content: 'You answer in exactly one sentence.' },
    { role: 'user', content: 'What are you?' },
  ],
})

The same, without an SDK (the Bearer header only when you set apiKey):

curl -s http://127.0.0.1:3080/v1/chat/completions \
  -H 'content-type: application/json' -H 'Authorization: Bearer local-key' \
  -d '{"user":"webtroll","messages":[{"role":"user","content":"hello"}]}'

Request parameters

fieldrequiredwhat it does
messagesyesNon-empty array. Roles: user, assistant, system (and developer, mapped to system). Content: a string, or an array of {"type":"text","text":…} parts. First request for a session (shared mode, or every per_request): the full array is rendered into one labeled transcript that seeds the new session. Later shared-mode request: only the latest non-empty user turn is admitted — the session already holds the earlier history, and a follow-up with no user turn is a 400.
usernoThe agent preset id the session joins: anything in ~/.dsh/.agent-presets/, plus the shipped presets. Absent → the profile default preset (the web bundle's standard). Unknown id → 400 listing the roster; a preset discovery reports broken → 400 with the reason. The preset is resolved at session creation only: in shared mode a session keeps the preset its first request chose — a different user value always means a different session (and therefore its own preset).
sessionno"shared" (the default — omit it): one DSH session per user value; requests without user share one default-preset session. "per_request": a fresh session for every prompt, seeded from the full messages array each time.
modelnoSession-creation detail. Shared mode: the first request's value sets the session's agent options; later requests' values are ignored on purpose. per_request: applies to that request's session. Absent (or equal to the configured wire id) → the host's default model selection; any other id is used as the session's real model on the configured (or host default) provider.
max_tokens / max_completion_tokensnoPositive integer; same creation-time semantics as model.
streamnoBoolean. true → Server-Sent Events: chat.completion.chunk frames, then .

Rejected with a 400 (the OpenAI error envelope): tools, tool_choice, functions, function_call (function calling is not supported — the agent still uses its own tools server-side, they just don't appear on the wire), tool-role messages, image or other non-text content parts, n ≠ 1, and unknown stream_options keys.

Responses

  • Only committed assistant text leaves the wire — each committed message's text blocks, in order. Reasoning and tool calls stay off it.
  • Streaming granularity is per committed message, not per token — one chat.completion.chunk per committed assistant message, then a finish chunk, then [DONE].
  • finish_reason: a turn ended max-tokens → length; everything else that settles (completed, aborted, interrupted, blocked) → stop. Error endings never produce a finish — the request gets a 5xx.
  • usage: DSH token counts are disjoint, so prompt_tokens = input + cache-read + cache-write, completion_tokens = output + reasoning.
  • Errors use the OpenAI envelope: {"error": {"message", "type", "param": null, "code": null}} — a 400 for wire violations, 401 for a missing/wrong bearer key, 409 when a second request arrives for a shared key that is still in flight (there is exactly one prompt per key at a time; per_request has no 409s), and 5xx when the turn itself fails.
  • Disconnecting mid-request cancels the in-flight turn. A non-stream request answers 500; a stream simply ends without [DONE] so SDKs raise instead of silently truncating.
  • per_request sessions are kept alive after the response — never reused, disposed only at plugin teardown.

GET /v1/models returns the single configured model id ({"object":"list","data":[{"id": …}]}).

Development

# tests (32: wire unit tests + a fake-ctx integration smoke)
node --import ./test/register.mjs test/test.mjs

# typecheck (repo toolchain, strict)
TSC=/src/misc/harness/deepseek-harness/node_modules/.bin/tsc
"$TSC" --noEmit --strict --noUnusedLocals --noUnusedParameters \
  --noFallthroughCasesInSwitch --module nodenext --target es2023 \
  --allowImportingTsExtensions --skipLibCheck \
  --typeRoots /src/misc/harness/deepseek-harness/node_modules/@types --types node \
  openai-api.ts

Layout: openai-api.ts (entry: plugin surface, session bookkeeping, HTTP routes, settlement), src/wire.ts (pure wire layer: parsing, transcript rendering, framing, usage/finish mapping), src/config.ts (hand-rolled Standard-Schema v1 config validator). No runtime dependencies beyond the DSH packages resolvable from the CLI's node_modules.

data: [DONE]
stream_options.include_usagenoBoolean. When true, a trailing usage chunk (choices: []) precedes [DONE].
nnoMust be 1 or absent.