dsh-api-expand
Verifieddsh-api-expand · v0.1.2 · MIT · Web UI
DeepSeek Harness 插件:把 DSH 的会话 / 模型 / 工作区能力以带 Bearer token 鉴权的 HTTP 接口对外暴露(/api-expand/run、/api-expand/inspect),并可在「设置 → API 扩展」面板在线配置路径与超时。
Install
dsh plugin add dsh-api-expand Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-api-expand
DeepSeek Harness 插件:把 DSH 的会话 / 模型 / 工作区能力,以一个带 token 鉴权的 HTTP 接口对外暴露。 调用方(Spring Boot、脚本、其他服务)带上标准的
Authorization: Bearer <token>即可直接驱动 DSH 跑一轮对话,无需自己接 LLM SDK。
- 支持版本:
dsh 0.2.0-rc.2(package.json→dsh.supported同步声明) - 零 npm 依赖:运行时依赖
@deepseek-ai/*全部由 DSH 宿主解析,安装即用
为什么用 dsh-api-expand?
把 DSH 当成一个「可被程序调用的大脑」:
- 一个接口跑一轮对话:
POST /api-expand/run,传prompt即可;返回这一轮的完整结果。 - 自带鉴权:口令存在凭据域(
.credentials.yaml),只在「设置 → API 扩展」面板里填写,接口侧按 RFC 6750 的Authorization: Bearer <token>校验,失败回 401 +WWW-Authenticate挑战,Spring Security / WebClient / Feign 都能按标准识别。 - 面板在线配置:接口路径、超时都能在面板改,保存即生效,不用重启、不影响正在跑的那一轮。
- 可自检:
POST /api-expand/inspect返回端口、默认模型、默认工作区、可选模型列表,用来确认插件是否加载正常。
安装
方式一:Web 界面(推荐)
DSH Web → 设置 → 插件,填入包名 dsh-api-expand → 安装。走的是与 dsh plugin add 相同的 pnpm 路径。
方式二:npm
dsh plugin --profile web add dsh-api-expand
# 指定版本:dsh plugin --profile web add [email protected]
升级必须写版本号。 只写包名时,pnpm 会沿用 lockfile 里已锁定的版本(日志表现是
reused N, downloaded 0), 于是“重装”装的还是旧代码。升级用dsh plugin --profile web add dsh-api-expand@<新版本>或dsh plugin --profile web add dsh-api-expand@latest,装完核对node_modules/dsh-api-expand/package.json的version。
方式三:本地开发(link)
dsh plugin --profile web add link:<本仓库绝对路径>
本地
link:安装时,插件目录需要能解析到@deepseek-ai/*,否则 symlink 被 realpath 后无法定位宿主包。
注册与生效
包内 package.json 声明了 dsh.bundle.patch,指向随包发布的 cordis.patch.yml。安装时 DSH 会把本包写入
$DSH_HOME/profiles/<profile>/package.json 的 dsh.profile.bundles,该 patch 随即自动 insert 插件的 host 行:
# cordis.patch.yml —— 随包发布,bundle 被选中后自动生效
- insert:
- id: dsh-api-expand
name: 'dsh-api-expand'
config:
timeoutMs: 1200000
然后重启 dsh web,并硬刷新浏览器(Ctrl/Cmd + Shift + R)。插件集变更必须重启进程才进 client roster。
⚠️ 不要在自己的 profile
cordis.patch.yml(或机器级)里再 insert 同一id:同一 id 由两层声明会让 loader 组合失败。 从开发期的file://手工注册迁移到 npm 安装时,请先删掉旧的id: api-expand行。
使用
1. 配置 token
DSH Web → 设置 → API 扩展,在「接口 token」里填一个口令并保存(留空表示保持当前 token,不会清空)。
未配置 token 时,接口返回 401(code -2)并提示去面板填写。
调用方在请求头里带上:
Authorization: Bearer <your-token>
2. 发起会话
curl 'http://127.0.0.1:<port>/api-expand/run' \
-H 'Authorization: Bearer <your-token>' \
-H 'content-type: application/json' \
-d '{"prompt": "你好,介绍一下你自己"}'
请求体字段
| 字段 | 必填 | 说明 |
|---|---|---|
prompt |
是 | 本轮指令。空 POST(不带 prompt)是冒烟检查,返回 code 0 / msg loaded |
workspace |
否 | Agent 干活的目录,须为已存在的绝对路径;不传则用本部署默认工作区 |
model |
否 | 格式 provider/model,取值见 inspect 返回的 models;不传用默认模型 |
sessionId |
否 | 带已有 sessionId 可续接同一会话;网页端正回答中的会话返回 409 |
preset |
否 | Agent 预设;续接空闲会话时不生效 |
timeoutMs |
否 | 本轮超时,覆盖配置默认值 |
3. 查看部署信息
curl 'http://127.0.0.1:<port>/api-expand/inspect' -H 'Authorization: Bearer <your-token>'
返回:port、timeoutMs、defaultModel、defaultWorkspace(id + 路径)、presets、models。
不发起会话,可用来确认插件是否加载。
返回与状态码
统一 JSON:{ code, msg, ... }。code:0 成功 / -1 参数或错误 / -2 未配置 token。
| HTTP | 含义 |
|---|---|
| 200 | 正常 |
| 400 | workspace / model / sessionId 不合法 |
| 401 | 鉴权失败。三种情况都回 401,用 code 区分:-2 token 尚未配置(去面板填写)/-1 缺少或格式错误的 Authorization 头/-1 口令不匹配。响应同时带 WWW-Authenticate: Bearer realm="dsh-api-expand",后两种分别附 error="invalid_request"、error="invalid_token" |
| 405 | 方法不对(run 只收 POST,inspect 只收 GET) |
| 409 | 目标会话正在回答中 |
| 500 | 本轮执行出错 |
| 504 | 本轮超时 |
4. Spring Boot 调用
标准 Bearer 头,WebClient、RestTemplate、OpenFeign 都能直接接:
# application.yml
dsh:
api-expand:
base-url: http://127.0.0.1:3080
token: ${DSH_API_EXPAND_TOKEN} # 与面板里填的口令一致
@Configuration
class DshClientConfig {
@Bean
WebClient dshWebClient(@Value("${dsh.api-expand.base-url}") String baseUrl,
@Value("${dsh.api-expand.token}") String token) {
return WebClient.builder()
.baseUrl(baseUrl)
// 标准 Bearer 凭据;WebClientResponseException.Unauthorized 对应 401
.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.codecs(c -> c.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
.build();
}
}
record DshRunRequest(String prompt, String workspace, String sessionId,
String model, String preset, Integer timeoutMs) {}
record DshRunData(String sessionId, Boolean resumed, String workspaceId,
String reply, String error, Boolean attached, String partialReply) {}
record DshResponse(Integer code, String msg, DshRunData data) {}
@Service
class DshService {
private final WebClient client;
DshService(WebClient dshWebClient) { this.client = dshWebClient; }
/** 单轮对话:不带 sessionId 就是新会话,带上返回的 sessionId 可续接。 */
String run(String prompt, String sessionId) {
DshResponse res = client.post()
.uri("/api-expand/run")
.bodyValue(new DshRunRequest(prompt, null, sessionId, null, null, null))
.retrieve()
.bodyToMono(DshResponse.class)
// 一轮最长 20 分钟,读超时要盖得住,否则客户端先断、Agent 白跑
.block(Duration.ofMinutes(25));
if (res == null || res.code() != 0) {
throw new IllegalStateException("dsh 调用失败:" + (res == null ? "空响应" : res.msg()));
}
return res.data().reply();
}
}
RestTemplate版本:HttpHeaders#setBearerAuth(token)即可,别手写"Bearer " + token漏掉空格。 一轮可能跑很久,记得把SimpleClientHttpRequestFactory的readTimeout调到分钟级(或换成HttpComponentsClientHttpRequestFactory)。
配置项
| 键 | 类型 | 默认 | 说明 |
|---|---|---|---|
timeoutMs |
number | 1200000 |
单轮超时(1000–3600000 ms),面板可改 |
runPath |
string | /api-expand/run |
发起会话的接口路径,面板可改,保存即生效 |
inspectPath |
string | /api-expand/inspect |
查看部署的接口路径,面板可改 |
token 不在上述配置里:它按固定变量名 DSH_API_EXPAND_TOKEN 存在凭据域,面板只能填值、看不到也改不了变量名。
开发
# 语法/加载自检(不依赖测试框架)
node --check index.js && node --check client.js
本地测试(不发布 npm)
装好的插件在 <DSH_HOME>/profiles/web/node_modules/dsh-api-expand/。改完源码有两种验证方式:
方式 A:覆盖已装副本(改 UI 最快)
# 把改过的文件复制进已安装的包(先删再拷,避免破坏 pnpm 的硬链接)
cp index.js client.js cordis.patch.yml package.json \
<DSH_HOME>/profiles/web/node_modules/dsh-api-expand/
- 只改
client.js:dsh-client-hmr会监听 bundle 并推送重建帧,浏览器刷新即可看到效果,不用重启 dsh。 - 改了
index.js/cordis.patch.yml/package.json:宿主 HMR 跳过node_modules,必须重启dsh web。 - 这两种情况都建议硬刷新(
Ctrl + Shift + R),Safari 用「清空缓存后重新载入」。
方式 B:打 tarball 走真实安装链路
npm pack # 得到 dsh-api-expand-0.1.2.tgz
dsh plugin --profile web add file:<tgz 的绝对路径>
然后重启 dsh web。这条路验证的是发布产物本身(files 白名单、exports、bundle patch 是否齐全)。
排查「设置里没有面板」
按顺序确认三件事,任一不满足面板都不会出现:
- 宿主行在组合结果里:
dsh --profile web --dump-config | grep -A4 dsh-api-expand,应当看到id: dsh-api-expand。 没有就是 bundle 没被选中(dsh.profile.bundles)或被跳过 —— 启动时 stderr 会打印skipping profile bundle ...及原因。 - 客户端进入 roster:打开页面源码能看到
dsh-api-expand/client.js与"id":"dsh-api-expand"。 - 行 id 与面板寻址一致:面板用
ROW_ID调configForms.get/whileServed,这个值必须等于宿主行的id(当前dsh-api-expand);对不上时面板会一直等一个永不被服务的命名空间,设置里什么都不显示,也不报错。
接口侧是否加载用 curl .../api-expand/inspect 即可:能返回 code 0 说明宿主半已挂载。
许可证
MIT © geoffwo