DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Model Endpoint — DeepSeek Harness 插件(DSH Plugin)
← Plugins
M

dsh-model-endpoint

Model Endpoint

将此 DeepSeek Harness 配置文件使用的模型发布为兼容 OpenAI 的 HTTP 端点,并绑定到可配置的局域网或公共地址。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:Xxsttb/dsh-model-endpoint#e391fd86e03f3f241efc991201a51b214e1e4da8
README兼容性版本

兼容性与来源证明

Model Endpoint 以 dsh-model-endpoint 发布,当前版本为 1.0.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/13

版本

1.0.0stable
2026/10/13

相关插件

正在加载相关插件…

最新版
1.0.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/16
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 integrations-communication 分类下经过校验的插件。

Acp App@deepseek-ai/dsh-acp-appdsh ACP 配置文件包:基于 dsh-base 的仅限自动化的 JSON-RPC stdio 和进程生命周期管理Ab Publishdsh-ab-publish浏览器自动化发布工具,可登录任意网站并发布内容,整合为一个可发布的插件包Ab Wechat Scrapedsh-ab-wechat-scrape--- description: "树外 wechat_scrape 工具:获取微信公众号文章,过滤推广区块,并将每篇文章保存为 Markdown 文件,供此机器的 dsh 配置文件维护者使用。" kind: "package-reference" ---Remote Web Ui@linxin666/dsh-remote-web-ui通过扫码配对访问 dsh Web GUI,共享一个官方界面:设置按钮旁的二维码可将手机和 PC 配对到同一个 Web GUI(手机采用竖屏触控适配层,PC 使用完整桌面界面),通过一次性令牌和 r

README

dsh-model-endpoint

Publishes the model your DeepSeek Harness profile is already using as an OpenAI-compatible HTTP endpoint.

Point any OpenAI client — the openai SDK, LangChain, Cline, a shell script, another Harness instance — at the base URL this plugin prints, and it answers with whichever model you have selected in chat or on the Models settings page. No second credential store, no extra configuration on the client side beyond a base URL.

Because the listener is created by the plugin itself (not by the Harness web server), the bind address is yours to choose: 127.0.0.1 keeps it on this machine, 0.0.0.0 publishes it to your LAN, and the same port behind a router or NAT forward is reachable from the public internet. Nothing in the request path depends on which of those three topologies you use.


What it does

GET /v1/modelsLists every model this profile can route, as provider/model and as a bare id when unambiguous.
GET /v1/models/<id>One model object, or 404 model_not_found.
POST /v1/chat/completionsOpenAI chat completions, streaming (SSE) and non-streaming.
GET /healthLive bind state, reachability per topology, auth mode, current model.
GET /The same document as /health.

Also served, on the Harness web server rather than this endpoint:

GET /plugins/model-endpointThe same status document, same-origin, for the settings page.
GET /plugins/model-endpoint/modelsThe status document plus the reachable-model catalog.
GET /plugins/model-endpoint/efforts?provider=&model=The reasoning efforts that route declares, for the settings dropdown.
GET /plugins/model-endpoint/toolsEvery registered tool with its switch state, for the settings page.

Model resolution

The endpoint answers with, in order of precedence:

  1. The model field of the request, if it names a routable model. Both provider/model and a bare model id are accepted.
  2. The profile's current selection — the same value the chat picker and the Models settings page write. Switch models in chat, and the endpoint switches with you.
  3. The fallback provider and model on this plugin's settings page.
  4. Whatever is selected, even when followSelection is off, as a last resort. An endpoint that answers with the model you are looking at beats one that answers with a 503.

If nothing is routable, the response is 503 model_unavailable and the message names every candidate that failed and why.

Reasoning effort

reasoning_effort is honoured only when the served model actually declares that effort; otherwise the model's own default applies. This is why an unknown effort degrades quietly instead of failing the request. The setting on the plugin's page supplies a default for requests that omit the field.

Both the fallback model and the reasoning effort are dropdowns, not free-text fields. They are filled from the same catalog the Models settings page reads, so what you can pick is exactly what this profile can route to, and the effort list belongs to the model you actually picked. A value already stored that is no longer offered — a provider that went offline, an effort its model stopped declaring — stays selectable and is labelled rather than being silently rewritten.

Streaming

POST /v1/chat/completions with "stream": true answers with SSE. Text arrives as delta.content, reasoning as delta.reasoning_content (the extension DeepSeek clients already understand), and tool calls as the usual indexed delta.tool_calls. A keep-alive comment is sent while the model is thinking, so proxies do not time the connection out. Usage is reported in a final chunk carried with the terminal finish_reason.

Tool calling

Two different things are called "tool calling" here, and they compose.

Caller-supplied tools. tools is forwarded to the model, tool calls come back on both the streaming and non-streaming paths, and role: "tool" messages are accepted on the way in. The endpoint never executes a tool it was handed by a request — those belong to the caller, and the call is returned with finish_reason: "tool_calls" for the caller to run. Function calling therefore works with clients that rely on it.

Borrowed tools. The endpoint can also lend the model the tools this Harness has registered, run them itself, and feed the results back — a full model/tool round trip inside one request. That is real capability over this machine, so it is off by default and, once on, only the individual tools switched on are sent. See Borrowed tools.

A request that supplies its own tools never receives borrowed ones. Mixing them would hand the caller back calls it cannot run.


Borrowed tools

Settings → Model endpoint → Lend this profile's tools to the model.

When it is on, every tool the Harness has registered is listed by name with its own switch. Only switched-on tools are sent to the model. When the model calls one, the endpoint executes it through the Harness tool runtime, appends the result, and asks the model again — up to Max tool rounds (default 8). A turn that hits the limit is cut off with 409 tool_round_limit rather than being retried forever.

Granularity, stated honestly. The switches are per tool, not per plugin. The Harness registry records a tool's name, description, and parameters; it does not record which plugin registered it, and the scope a tool is registered under is the calling agent's, not the plugin's. A per-plugin switch would therefore require guessing at attribution this plugin cannot observe. Per-tool is finer than per-plugin and fails closed, so that is what is offered.

Some tools run code, read or write files, or reach the network. Switch on the ones you would be willing to run yourself for an unauthenticated caller — and if the listener is reachable beyond 127.0.0.1, set an API key as well.

curl http://127.0.0.1:8787/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{"model":"auto","messages":[{"role":"user","content":"List the files in the workspace."}]}'

The model decides whether to call a borrowed tool; nothing forces it.


Install

From a checkout, or from a packed tarball:

dsh plugin --profile desktop add /absolute/path/to/dsh-model-endpoint
# or
pnpm pack --pack-destination dist
dsh plugin --profile desktop add /absolute/path/to/dist/dsh-model-endpoint-1.0.0.tgz

The plugin mounts itself, starts its listener, and adds a Model endpoint page under Settings. Nothing else is required — the Harness exposes no inbound HTTP API of its own, so this plugin owns its listener end to end.

Verify:

curl http://127.0.0.1:8787/health
curl http://127.0.0.1:8787/v1/models

Uninstall:

dsh plugin --profile desktop remove dsh-model-endpoint

Two things worth knowing

Install with dsh plugin add, not by hand. Editing the profile manifest yourself leaves the running process with a stale module-resolution table: the plugin loads as a dead row — present in the composition, never activated, no error anywhere — until the process restarts. This is not specific to this plugin.

Never install a plugin with link: and later replace it with a tarball. pnpm removes the old node_modules entry, and on Windows it follows the reparse point, which empties the source directory the link pointed at. Install one way and stay there.


Settings

Settings → Model endpoint.

FieldDefaultMeaning
Run the endpointonTurn the listener off without uninstalling.
Bind address127.0.0.10.0.0.0 accepts connections on every interface.
Port8787The port clients connect to.
API keyemptyWhen set, clients send Authorization: Bearer <key>.
Follow the selected modelonServe whatever is picked in chat or on the Models page. While on, the fallback model dropdown is hidden because it is not what gets served.
Fallback provider / modelemptyUsed when not following the selection, or when nothing is selected. Chosen from a dropdown of the models this profile can actually reach.
Reasoning effortmodel defaultOnly applied when the served model declares it. The dropdown lists exactly the efforts the chosen fallback model declares, plus the model's own default.
Extra system promptemptyPrepended only for requests that carry no system message of their own.
Default max output tokens8192Applied when a request omits max_tokens.
Request timeout (ms)600000Upper bound for one completion, including streaming.
Allowed browser origin*Sent as Access-Control-Allow-Origin.
Lend this profile's toolsoffHand the model the Harness tools switched on below, and run them here.
(per tool)offOne switch per registered tool. Only tools that are on are sent.
Max tool rounds8Model/tool round trips one request may take before it is cut off.

The API key row has a Generate button that fills the field with a fresh 48-character key drawn from a 62-symbol alphabet with rejection sampling (about 286 bits, no +///= to escape and no look-alike pairs). It only fills the field — press Save to apply it, and copy it out first, because this field is the only place it is stored.

Every field is live: change one and the listener rebinds on its own. The status block above the fields shows the actual bound port and the addresses this machine is reachable at, read from the running listener rather than echoed back from the form.

Security

Leave the API key empty only while bound to 127.0.0.1. The settings page warns when the endpoint is bound beyond this machine without a key, because in that configuration anyone who can reach the port can spend this profile's model quota. API keys are compared in constant time.

There is no TLS. If you expose this to the public internet, terminate TLS at your reverse proxy, or keep the connection inside a tunnel — either way the endpoint itself speaks plain HTTP.


Connecting a client

Any OpenAI-compatible client:

from openai import OpenAI

client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="unused")
client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "hello"}],
)

model: "auto" (or "default", or omitting the field) means "use whatever this profile has selected". Any non-empty string works as the API key when authentication is off.

With curl:

curl http://127.0.0.1:8787/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{"model":"auto","messages":[{"role":"user","content":"hello"}],"stream":true}'

Reaching it from another machine

  1. Set Bind address to 0.0.0.0 and give the endpoint an API key.
  2. Read the Local network URL from the status block — that is this machine's address on your LAN, with the port already filled in.
  3. From the internet, forward that port on your router to this machine and use your public address. The endpoint needs no knowledge of the forward; a request that arrives is a request that is answered.

If the Host log says the listener could not bind, the port is taken or the bind address does not exist on this machine. The settings page surfaces this as "Unknown" status rather than silently pretending to run.


Errors

Errors use OpenAI's envelope:

{"error": {"message": "...", "type": "invalid_request_error", "code": "model_not_found"}}
StatusCodeCause
400invalid_request_errorMalformed message, role, tool, or body.
400unsupported_contentA non-text content part. This endpoint serves text-only models.
401invalid_api_keyMissing or wrong bearer token.
404not_foundNo such route.
404model_not_foundThe requested model is not routable.
405method_not_allowedWrong method for a known route.
409tool_round_limitThe model kept calling borrowed tools past Max tool rounds.
413request_too_largeBody above 8 MiB, or an oversized input.
429rate_limit_exceededUpstream provider rate limit.
429insufficient_quotaUpstream quota exhausted.
500upstream_errorThe model call failed without a more specific code.
503model_unavailableNothing routable could be resolved.

Upstream failures keep the Harness's own classification, so AUTH, RATE_LIMIT, QUOTA, ACCOUNT_QUOTA and CONTEXT_WINDOW_EXCEEDED arrive as the codes above rather than being flattened into one generic error.


Layout

cordis.patch.yml    profile entry: id `model-endpoint` — also its settings namespace
lib/index.js        Host half: the HTTP listener and the OpenAI translation
lib/client.js       Client half: the settings page
locale/{en,zh}.json display metadata for the plugin list
icon.svg            plugin icon

The profile entry id in cordis.patch.yml is load-bearing: the settings service addresses a plugin by its entry id, so the settings namespace and the id must match. Changing one without the other detaches the settings page.


Development

Syntax check both halves:

ELECTRON_RUN_AS_NODE=1 "/path/to/DeepSeek Harness" --check lib/index.js
ELECTRON_RUN_AS_NODE=1 "/path/to/DeepSeek Harness" --check lib/client.js

Pack:

pnpm pack --pack-destination dist

The Host half is ESM and imports @deepseek-ai/schemastery and @deepseek-ai/dsh-llm. Both resolve through the Harness installation, so the package declares them as peer dependencies and node_modules/ stays out of the repository.


AI-generated

Everything in this repository was generated by an AI assistant, at the request of the repository owner:

  • the Host half (lib/index.js), including every request handler, the OpenAI translation layer, and the listener lifecycle;
  • the Client half (lib/client.js), including the settings page, its styles, the capsule switches, the random-key generator, and the English and Chinese copy;
  • the settings page's extra-system-prompt field and its empty default — the endpoint deliberately injects nothing of its own, so a client's own system message survives untouched;
  • this README, the plugin manifest, cordis.patch.yml, and icon.svg;
  • every code comment in every file.

It was written against DeepSeek Harness 0.2.0-rc.2 by reading that installation's own source, and verified live against a running profile: the listener binds, /v1/models enumerates the profile's providers, /v1/chat/completions streams real completions, and the settings page reads and writes through the settings service. The borrowed-tool loop was verified against a harness that drives the Host half with a fake tool registry, covering a tool that runs, a tool that fails, a call named only on a later delta, malformed arguments, a model loop that hits the round limit, and a caller-supplied tool that must never be mixed with a borrowed one.

It is not copied from any existing plugin. It may still contain mistakes, and it is offered without warranty. Read it before you expose it to a network.


License

MIT — see LICENSE.