Introduction¶
When driving dsh using scripts, desktop shells, or editor plugins, a common problem arises: capabilities such as changing language, switching workspaces, and sensing when the agent is idle are all embedded within the dsh process. External processes either directly reach in to call its in-process services, or write their own channels, resulting in inconsistent interfaces and heavy coupling.
dsh-api’s approach is to converge this into a single entry point: dsh already listens on HTTP at 127.0.0.1, so plugins can directly mount a set of /dsh-api/* JSON routes on this socket. Any process on the same machine—desktop wrappers, browser extensions, CLI, editor integrations—goes through here. Below, I introduce its positioning, interface, installation, and common issues.
What is it¶
dsh-api is a dsh plugin maintained by lilming123, current version 0.1.0, MIT license. It exposes dsh’s internal capabilities—language settings, workspace registry, companion bridging—as JSON routes on the port dsh is already listening on at 127.0.0.1. It does not open new ports, has zero runtime dependencies other than Node itself, and the route prefix defaults to /dsh-api.
The DSH ecosystem philosophy is “everything is a plugin,” and this plugin follows this premise: install into the profile, and load automatically with dsh web.
Core Feature: Two-Layer Routing¶
Routes are divided into two layers, mounted under the same /dsh-api prefix.
Native Routes¶
Available immediately after the plugin loads, without depending on any extra process:
| Method | Path | Purpose |
|---|---|---|
| GET | /dsh-api/health |
Liveness check and basic identity info (dsh port, cwd, companion registration status) |
| GET | /dsh-api/language |
Read locale.preference |
| POST | /dsh-api/language |
Write locale.preference (zh or en) |
| GET | /dsh-api/workspace/list |
List all workspaces from workspaceRegistry |
| GET | /dsh-api/workspace/current |
Current cwd and companion snapshot |
| POST | /dsh-api/workspace/create |
Register a new workspace with { path, title? } |
| GET | /dsh-api/events |
SSE event stream |
Event Stream¶
GET /dsh-api/events is a SSE long connection. There are five types of events:
ready: Connection established;agent-idle: Triggered when anyagent/statusevent transitions fromrunningtoidle, carrying fields likesessionId,title,previousStatus, etc.;approval-needed: A read-only bypass of the dshapproval/requestwaterfall—the plugin observes the request, broadcasts a summary, and hands control back to the real response chain;heartbeat: Every 25 seconds, to prevent disconnection due to agent idleness;server-stopping: Broadcasted before dsh shuts down, after which the socket closes.
Companion Bridging Routes¶
The second layer requires a registered companion. Companion refers to any local process: write { port, token, pid, ... } to $DSH_HOME/dsh-api-companion.json and implement the /companion/* protocol. When no companion is registered, these routes return 503, while native routes are unaffected:
| Method | Path | Purpose |
|---|---|---|
| GET | /dsh-api/companion/state |
Companion state snapshot |
| POST | /dsh-api/workspace/open |
Switch dsh cwd (restarts dsh under companion) |
| POST | /dsh-api/input/paste |
Inject text into the dsh UI |
| POST | /dsh-api/window/show |
Focus the host window |
| POST | /dsh-api/window/reload |
Reload the host window |
| POST | /dsh-api/app/quit |
Quit the host application |
Installation and Enablement¶
First install the plugin, then confirm the loading method:
dsh plugin --profile web add github:lilming123/dsh-api
The README also lists the npm form (dsh plugin --profile web add dsh-api), but notes it as “once published,” so current availability depends on the repository README.
Behavior after installation:
dsh pluginis a pnpm thin wrapper; the package falls into$DSH_HOME/profiles/<profile>/node_modules/;dsh-apiis registered into the bundle list of that profile;- The next time
dsh webloads automatically, no--patchis needed.
Node version requirement >= 20. Additionally, the desktop client dsh-desktop will skip its built-in fallback version if it detects an installed dsh-api; you only need to manually install this plugin when directly driving dsh without dsh-desktop.
Configuration¶
The plugin has two configuration items, written in the loader entry’s config:
| Key | Default Value | Purpose |
|---|---|---|
basePath |
/dsh-api |
HTTP route prefix |
companionFile |
$DSH_HOME/dsh-api-companion.json |
Companion discovery file, read on demand |
For example, to change the route prefix to /control, write in $DSH_HOME/profiles/web/cordis.patch.yml:
- id: dsh-api
config:
basePath: /control
This way, all routes start with /control.
Typical Usage¶
Following the steps above, the plugin is loaded with dsh web. Below, I walk through common interfaces using curl (the port continues to be 3181 as in the README example; adjust according to your actual startup parameters).
- Liveness check:
curl http://127.0.0.1:3181/dsh-api/health
Returns dsh port, cwd, companion registration status, etc., useful for confirming the plugin is loaded.
- Subscribe to event stream:
curl -N http://127.0.0.1:3181/dsh-api/events
-N tells curl not to buffer, directly printing SSE frames: ready, agent-idle, approval-needed, and the heartbeat every 25 seconds.
-
Write language preference and register workspace:
POST /dsh-api/languageaccepts{ "language": "zh" | "en" };POST /dsh-api/workspace/createaccepts{ path, title? }, wherepathpoints to the directory to register andtitleis optional. -
Run example scripts: There are two directly runnable files under the repository
examples/,curl.shiterates through all endpoints, andevents.mjsis a zero-dependency Node SSE subscription example suitable as a starting point for your own integration. -
Debugging with development mode:
git clone https://github.com/lilming123/dsh-api.git
cd dsh-api
mkdir -p "$DSH_HOME/profiles/web/dsh-api-dev"
ln -sf "$PWD/index.mjs" "$DSH_HOME/profiles/web/dsh-api-dev/index.mjs"
cat > /tmp/dsh-api-dev.patch.yml <<'YML'
- insert:
- id: dsh-api-dev
name: ./dsh-api-dev/index.mjs
YML
dsh web --patch /tmp/dsh-api-dev.patch.yml --port 3181
dsh-api is a pure ESM plugin with no build steps. The approach is to clone index.mjs and symlink it into the profile’s dsh-api-dev/ directory, then use --patch to insert it into the loading list and specify port 3181 to start.
Security Model¶
Three types of constraints are worth knowing:
- Binds only to 127.0.0.1 which dsh is already listening on; does not open new ports, so the internet cannot access it;
- Change requests validate the
Origin: Origins without an Origin header (CLI scenario) or loopback origins are allowed; others return 403; - Companion bridging routes forward the token from the discovery file via the
x-dsh-api-companion-tokenrequest header, which is validated on the companion side; mismatches result in rejection.
FAQ¶
Check by symptom:
- All
/dsh-api/*return 404: Plugin not loaded. Check if~/.dsh/profiles/web/package.json’sdsh.profile.bundlescontainsdsh-apiand if~/.dsh/profiles/web/node_modules/dsh-apiexists; run the installation command again if either is missing; POST /dsh-api/workspace/createreturns 503: Current dsh context is missingworkspaceRegistry—either the dsh version is older than this service, or the profile patch removed it. Upgrade dsh (npm i -g @deepseek-ai/dsh) and try again;- Companion routes return 503: No companion registered. This is expected behavior when running dsh directly without a wrapper like
dsh-desktop; native routes (/health,/language,/workspace/*,/events) remain usable; - SSE stream disconnects every ~60 seconds: Reverse proxy kills the connection while idle. The plugin sends a heartbeat every 25 seconds; if it still disconnects, increase the proxy idle timeout or remove the proxy—dsh-api binds to 127.0.0.1 and doesn’t strictly need a proxy.
Applicable Scenarios and Notes¶
Who is it for:
- Tool authors building desktop shells, browser extensions, or editor integrations for dsh, who need a unified HTTP entry point and don’t want to figure out in-process services on their own;
- Scripts that need to listen for agent idle events or approval events to perform external automation;
- Scenarios where you need to read or register workspaces from the outside, or switch dsh cwd.
Note: The plugin runs with the permissions of the current dsh process; through the exposed interfaces, it can access resources the dsh process can access. It is recommended to read the source code before installation (the main part is index.mjs, pure ESM with no build steps) and confirm that the MIT license fits your usage.
Summary¶
The value of dsh-api lies in convergence: reusing the existing 127.0.0.1 socket, a fixed set of /dsh-api/* JSON routes, plus an optional layer of companion bridging, providing a unified entry point for external tools to drive dsh. See the GitHub repository: https://github.com/lilming123/dsh-api; Community directory page (independent site, no official affiliation with DeepSeek or Huafang): https://www.skillhub.cn/plugins/lilming123/dsh-api.