前言¶
用脚本、桌面壳或编辑器插件驱动 dsh 时,常遇到一个问题:改语言、切工作区、感知 agent 什么时候空闲,这些能力都长在 dsh 进程内部。外部进程要么直接伸手调它的 in-process services,要么各写各的通道,接口不统一,耦合也重。
dsh-api 的做法是把这件事收敛到一个入口:dsh 本来就在 127.0.0.1 上监听 HTTP,插件直接在这条 socket 上挂一组 /dsh-api/* JSON 路由,同机的任何进程——桌面包装器、浏览器扩展、CLI、编辑器集成——都从这里走。下面介绍它的定位、接口、安装和常见问题。
这是什么¶
dsh-api 是 lilming123 维护的 dsh 插件,当前版本 0.1.0,MIT 许可证。它把 dsh 的内部能力——语言设置、工作区注册表、companion 桥接——以 JSON 路由的形式暴露在 dsh 已监听的 127.0.0.1 端口上。不新开端口,除 Node 本身外零运行时依赖,路由前缀默认 /dsh-api。
DSH 生态的理念是「一切皆插件」,这个插件遵循的正是这个前提:装进 profile,随 dsh web 一起加载。
核心功能:两层路由¶
路由分两层,挂在同一个 /dsh-api 前缀下。
原生路由¶
插件加载后即可用,不依赖任何额外进程:
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /dsh-api/health |
存活检查与基本身份信息(dsh 端口、cwd、companion 是否注册) |
| GET | /dsh-api/language |
读取 locale.preference |
| POST | /dsh-api/language |
写入 locale.preference(zh 或 en) |
| GET | /dsh-api/workspace/list |
列出 workspaceRegistry 的全部工作区 |
| GET | /dsh-api/workspace/current |
当前 cwd 与 companion 快照 |
| POST | /dsh-api/workspace/create |
以 { path, title? } 注册新工作区 |
| GET | /dsh-api/events |
SSE 事件流 |
事件流¶
GET /dsh-api/events 是一条 SSE 长连接,事件有五类:
ready:连接建立;agent-idle:任一agent/status事件从running转为idle时触发,携带sessionId、title、previousStatus等字段;approval-needed:对 dshapproval/request瀑布的只读旁路——插件观察请求、广播摘要,控制权原样交回真正的应答链;heartbeat:每 25 秒一帧,防止代理空闲断连;server-stopping:dsh 关闭前广播,之后 socket 关闭。
companion 桥接路由¶
第二层需要已注册 companion。companion 指任何本地进程:向 $DSH_HOME/dsh-api-companion.json 写入 { port, token, pid, ... },并实现 /companion/* 协议。未注册 companion 时,这些路由返回 503,原生路由不受影响:
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /dsh-api/companion/state |
companion 状态快照 |
| POST | /dsh-api/workspace/open |
切换 dsh cwd(companion 下重启 dsh) |
| POST | /dsh-api/input/paste |
注入文本到 dsh UI |
| POST | /dsh-api/window/show |
聚焦宿主窗口 |
| POST | /dsh-api/window/reload |
重载宿主窗口 |
| POST | /dsh-api/app/quit |
退出宿主应用 |
安装与启用¶
先装插件,再确认加载方式:
dsh plugin --profile web add github:lilming123/dsh-api
README 中还列出了 npm 形式(dsh plugin --profile web add dsh-api),但标注为 once published,当前是否可直接使用以仓库 README 为准。
安装后的行为:
1、dsh plugin 是 pnpm 薄封装,包落入 $DSH_HOME/profiles/<profile>/node_modules/;
2、dsh-api 注册进该 profile 的 bundle 列表;
3、下次 dsh web 自动加载,不需要 --patch。
Node 版本要求 >= 20。另外,桌面端 dsh-desktop 检测到已安装的 dsh-api 会跳过其内置回退版本;只有你直接驱动 dsh 时才需要手动装这份插件。
配置¶
插件有两个配置项,写在 loader entry 的 config 里:
| 键 | 默认值 | 用途 |
|---|---|---|
basePath |
/dsh-api |
HTTP 路由前缀 |
companionFile |
$DSH_HOME/dsh-api-companion.json |
companion 发现文件,按需读取 |
例如把路由前缀改成 /control,在 $DSH_HOME/profiles/web/cordis.patch.yml 写:
- id: dsh-api
config:
basePath: /control
这样所有路由就以 /control 开头。
典型用法¶
经过上面的步骤,插件已随 dsh web 加载。下面用 curl 走一遍常用接口(端口沿用 README 示例中的 3181,以你的实际启动参数为准)。
1、存活检查:
curl http://127.0.0.1:3181/dsh-api/health
返回 dsh 端口、cwd、companion 是否注册等信息,可用来确认插件已加载。
2、订阅事件流:
curl -N http://127.0.0.1:3181/dsh-api/events
-N 让 curl 不做缓冲,直接打印 SSE 帧:ready、agent-idle、approval-needed,以及每 25 秒一帧的 heartbeat。
3、写语言偏好与注册工作区:POST /dsh-api/language 接收 { "language": "zh" | "en" };POST /dsh-api/workspace/create 接收 { path, title? },path 指向要注册的目录,title 可选。
4、跑示例脚本:仓库 examples/ 下有两个可直接运行的文件,curl.sh 遍历所有端点,events.mjs 是零依赖的 Node SSE 订阅示例,适合作为自己集成的起点。
5、改源码调试时用开发模式:
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 是纯 ESM 插件,没有构建步骤。做法是把 clone 下来的 index.mjs 软链进 profile 的 dsh-api-dev/ 目录,再用 --patch 把它插进加载列表,指定 3181 端口启动。
安全模型¶
三类约束值得知道:
- 只绑定 dsh 已监听的 127.0.0.1,不新开端口,外网进不来;
- 变更类请求校验
Origin:无 Origin 头(CLI 场景)与回环 origin 放行,其余返回 403; - companion 桥接路由通过
x-dsh-api-companion-token请求头转发发现文件中的 token,由 companion 侧校验,不匹配即拒绝。
常见问题¶
按现象查:
/dsh-api/*全部 404:插件没加载。检查~/.dsh/profiles/web/package.json的dsh.profile.bundles是否含dsh-api、~/.dsh/profiles/web/node_modules/dsh-api是否存在,缺哪个就重跑安装命令;POST /dsh-api/workspace/create返回 503:当前 dsh 上下文缺workspaceRegistry——要么 dsh 版本早于该服务,要么 profile patch 把它裁掉了。升级 dsh(npm i -g @deepseek-ai/dsh)后再试;- companion 路由返回 503:没有注册 companion。直接跑 dsh 而不经
dsh-desktop之类的壳时这是预期行为,原生路由(/health、/language、/workspace/*、/events)照常可用; - SSE 流每约 60 秒断一次:反向代理在空闲杀连接。插件已每 25 秒发 heartbeat,仍断就调大代理空闲超时,或去掉代理——dsh-api 绑定 127.0.0.1,本来就不需要代理。
适用场景与注意¶
适合谁:
- 给 dsh 做桌面壳、浏览器扩展、编辑器集成的工具作者,需要一个统一 HTTP 入口,不想各自摸 in-process services;
- 需要监听 agent 空闲、审批事件做外部自动化的脚本;
- 想从外部读取或注册工作区、切换 dsh cwd 的场景。
注意:插件以当前 dsh 进程的权限运行,通过它暴露的接口能触达 dsh 进程能触达的资源。安装前建议先读一遍源码(主体是 index.mjs,纯 ESM,没有构建步骤),并确认 MIT 许可证符合你的使用方式。
小结¶
dsh-api 的价值在于收敛:复用 dsh 已有的 127.0.0.1 socket,一套固定的 /dsh-api/* JSON 路由,外加一层可选的 companion 桥接,让外部工具驱动 dsh 有了统一入口。仓库见 GitHub:https://github.com/lilming123/dsh-api;社区目录页(独立站点,与 DeepSeek、幻方无官方从属关系):https://www.skillhub.cn/plugins/lilming123/dsh-api。