Skip to content

dsh-api-expand

Verified

dsh-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 是否齐全)。

排查「设置里没有面板」

按顺序确认三件事,任一不满足面板都不会出现:

  1. 宿主行在组合结果里:dsh --profile web --dump-config | grep -A4 dsh-api-expand,应当看到 id: dsh-api-expand。 没有就是 bundle 没被选中(dsh.profile.bundles)或被跳过 —— 启动时 stderr 会打印 skipping profile bundle ... 及原因。
  2. 客户端进入 roster:打开页面源码能看到 dsh-api-expand/client.js 与 "id":"dsh-api-expand"。
  3. 行 id 与面板寻址一致:面板用 ROW_ID 调 configForms.get / whileServed,这个值必须等于宿主行的 id (当前 dsh-api-expand);对不上时面板会一直等一个永不被服务的命名空间,设置里什么都不显示,也不报错。

接口侧是否加载用 curl .../api-expand/inspect 即可:能返回 code 0 说明宿主半已挂载。

许可证

MIT © geoffwo