给 DeepSeek Harness 插件准备可复用的在线测试环境。按版本预构建 runtime…
31.5/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/28 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查未通过:仓库根目录及浅层子目录均无 dsh 插件清单,无法用 dsh 插件安装命令安装。以下命令仍可尝试,但可能失败。
无法用 dsh 插件命令安装(仓库内无 dsh 插件清单)
git clone https://github.com/cocofhu/dsh-testsuite.git按该仓库 README 的原生方式安装;若作者另行发布了 DSH 插件包,请以其文档为准
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · other
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 7 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(unknown · 2026/10/30) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装仓库根目录及浅层子目录均无 dsh 插件清单,无法用 dsh 插件安装命令安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-testsuite(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/11 22:13:44
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 106 处,展示前 106 条)
- 读取本地文件image/0.0.1-rc.5/patch-frontend.mjs:46
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.0.1-rc.5/patch-frontend.mjs:54
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.0.1-rc.5/patch-frontend.mjs:58
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.0.1-rc.5/patch-frontend.mjs:79
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.0.1-rc.5/patch-frontend.mjs:85
writeFileSync(index, patched); - 读取本地文件image/0.1.0-rc.2/patch-frontend.mjs:46
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.0-rc.2/patch-frontend.mjs:54
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.0-rc.2/patch-frontend.mjs:58
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.0-rc.2/patch-frontend.mjs:79
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.0-rc.2/patch-frontend.mjs:85
writeFileSync(index, patched); - 读取本地文件image/0.1.0-rc.3/patch-frontend.mjs:46
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.0-rc.3/patch-frontend.mjs:54
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.0-rc.3/patch-frontend.mjs:58
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.0-rc.3/patch-frontend.mjs:79
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.0-rc.3/patch-frontend.mjs:85
writeFileSync(index, patched); - 读取本地文件image/0.1.0-rc.6/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.0-rc.6/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.0-rc.6/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.0-rc.6/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.0-rc.6/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.0-rc.7/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.0-rc.7/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.0-rc.7/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.0-rc.7/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.0-rc.7/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.0-rc.8/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.0-rc.8/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.0-rc.8/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.0-rc.8/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.0-rc.8/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.1-rc.1/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.1-rc.1/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.1-rc.1/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.1-rc.1/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.1-rc.1/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.1-rc.2/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.1-rc.2/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.1-rc.2/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.1-rc.2/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.1-rc.2/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.2-alpha.2/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.2-alpha.2/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.2-alpha.2/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.2-alpha.2/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.2-alpha.2/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.2-alpha.3/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.2-alpha.3/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.2-alpha.3/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.2-alpha.3/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.2-alpha.3/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.2-alpha.4/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.2-alpha.4/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.2-alpha.4/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.2-alpha.4/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.2-alpha.4/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.2-alpha.5/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.2-alpha.5/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.2-alpha.5/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.2-alpha.5/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.2-alpha.5/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.2-rc.1/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.2-rc.1/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.2-rc.1/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.2-rc.1/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.2-rc.1/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.3-alpha.2/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.3-alpha.2/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.3-alpha.2/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.3-alpha.2/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.3-alpha.2/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.5-alpha.1/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.5-alpha.1/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.5-alpha.1/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.5-alpha.1/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.5-alpha.1/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.5-alpha.2/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.5-alpha.2/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.5-alpha.2/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.5-alpha.2/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.5-alpha.2/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.5-rc.1/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.5-rc.1/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.5-rc.1/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.5-rc.1/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.5-rc.1/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.5-rc.2/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.5-rc.2/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.5-rc.2/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.5-rc.2/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.5-rc.2/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.1.6-alpha.1/patch-frontend.mjs:43
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.1.6-alpha.1/patch-frontend.mjs:51
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.1.6-alpha.1/patch-frontend.mjs:55
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.1.6-alpha.1/patch-frontend.mjs:59
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.1.6-alpha.1/patch-frontend.mjs:65
writeFileSync(index, patched); - 读取本地文件image/0.2.1-rc.4/patch-frontend.mjs:19
for (const name of readdirSync(dir)) { - 读取本地文件image/0.2.1-rc.4/patch-frontend.mjs:22
const st = statSync(path); - 读取本地文件image/0.2.1-rc.4/patch-frontend.mjs:55
const src = readFileSync(path, "utf8"); - 写入本地文件image/0.2.1-rc.4/patch-frontend.mjs:63
writeFileSync(path, src.replaceAll(find, replace)); - 执行外部命令固定命令image/0.2.1-rc.4/patch-frontend.mjs:67
const root = execSync("npm root -g", { encoding: "utf8" }).trim(); - 读取本地文件image/0.2.1-rc.4/patch-frontend.mjs:71
const html = readFileSync(index, "utf8"); - 写入本地文件image/0.2.1-rc.4/patch-frontend.mjs:77
writeFileSync(index, patched); - 读取本地文件image/0.2.1-rc.4/patch-frontend.mjs:82
const hostSrc = readFileSync(hostConn, "utf8"); - 读取本地文件image/0.2.1-rc.4/patch-frontend.mjs:108
const src = readFileSync(file, "utf8"); - 写入本地文件image/0.2.1-rc.4/patch-frontend.mjs:110
writeFileSync(file, src.replaceAll(loopbackFind, "true")); - 发起网络请求web/app.js:71
const res = await fetch(path, {
扫描于 2026/11/2(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 一键创建在线测试容器
- 内置密钥与插件
- 插件测试套件
dsh 插件的测试套件
README
由 DeepSeek 最新模型翻译生成dsh-testsuite
给 DeepSeek Harness 插件准备可复用的在线测试环境。按版本预构建 runtime 镜像,在管理台登记后创建 Docker 容器,注入 API Key、提供方、模型和预装插件,然后打开 dsh web。
本项目是非官方工具,不是 DeepSeek Harness 的官方产品。
怎么工作
- 控制面(本仓库 Go 服务 + Web UI):登记镜像,创建 / 启停 / 重启 / 销毁环境,看日志。
- Runtime 镜像(每版本一份 image/<ver>/Dockerfile):在平台外用 make image 构建,或从 GitHub Release 推到 GHCR。已发布 tag 默认冻结。说明见 docs/images.md。
每个环境把容器内 3080 映射到宿主机随机端口。容器进入 running 后还会探测端口,Health 变为 Healthy 才允许「打开」(dsh Web 是根路径 SPA,启动还要几秒)。
镜像预先构建好,创建环境只选已登记镜像并注入配置。默认用 Docker;也可把 runtime 设为 kubernetes,由控制面在集群里创建 Deployment / Service / Ingress(见下方)。控制面不执行 docker build。
展开完整文档(README 剩余 1 节)
dsh-testsuite
给 DeepSeek Harness 插件准备可复用的在线测试环境。按版本预构建 runtime 镜像,在管理台登记后创建 Docker 容器,注入 API Key、提供方、模型和预装插件,然后打开 dsh web。
本项目是非官方工具,不是 DeepSeek Harness 的官方产品。
怎么工作
- 控制面(本仓库 Go 服务 + Web UI):登记镜像,创建 / 启停 / 重启 / 销毁环境,看日志。
- Runtime 镜像(每版本一份 image/<ver>/Dockerfile):在平台外用 make image 构建,或从 GitHub Release 推到 GHCR。已发布 tag 默认冻结。说明见 docs/images.md。
每个环境把容器内 3080 映射到宿主机随机端口。容器进入 running 后还会探测端口,Health 变为 Healthy 才允许「打开」(dsh Web 是根路径 SPA,启动还要几秒)。
镜像预先构建好,创建环境只选已登记镜像并注入配置。默认用 Docker;也可把 runtime 设为 kubernetes,由控制面在集群里创建 Deployment / Service / Ingress(见下方)。控制面不执行 docker build。 Docker 模式下,登记镜像时若本机没有该 tag,会从 GHCR 自动 pull。
快速开始
需要 Docker 和 Go 1.25+。
make image DSH_VERSION=0.2.1-rc.4 # 在平台外打 runtime;细节见 docs/images.md
cp config.example.yaml config.yaml # 本地配置,不要提交
make run # 控制面 http://127.0.0.1:8090
1. 打开「镜像版本」,选版本登记(本机没有会从 GHCR 自动 pull)。也可只 pull、稍后登记:docker pull ghcr.io/cocofhu/dsh-testsuite-runtime:0.2.1-rc.4。
2. 在「模型预设」填提供方、模型和 API 密钥(密钥只存在本机 data/)。
3. 回到「环境」,创建并等待 Health 变为 Healthy,再点「打开」。
首次推到 GHCR 的包默认是 private,需要在 GitHub Packages 设为 public。
用 Compose 把控制面也跑进容器(需挂 docker.sock;runtime 仍在宿主机构建或 pull):
make image DSH_VERSION=0.2.1-rc.4
docker compose up --build
创建环境
| 字段 | 说明 |
| dsh 版本 | 只能选「镜像版本」里已登记的条目 |
| 模型预设 | 在「模型预设」页在线填写(含 API 密钥)。创建环境时选预设即带上密钥,或选「手动填写」 |
| API 密钥 | 预设里保存;手动创建时现填。注入为 DSH_API_KEY;官方 provider 同时设 DEEPSEEK_API_KEY。列表只显示末 4 位 |
| Provider / Model | 写在预设里,或「手动填写」时现选。仅「自定义…」才填 Provider ID / API 地址 |
| 预装插件 | 每行一个 pnpm 源,如 github:owner/plugin#main。git 源的 prepare 会由 runtime 写入 profile 的 allowBuilds |
空闲 TTL 默认 2 小时(limits.idleTTL),超时会销毁容器和该环境的 $DSH_HOME。列表显示剩余 TTL,点「续期 6h」会从当前到期时间再延 6 小时。
控制面默认无认证,不要把 :8090 裸暴露到公网。config.yaml 和 data/ 含密钥,已 gitignore。
Kubernetes
把 runtime 设为 kubernetes 后,每个环境是同命名空间里的 Deployment + Service + Ingress,不再映射宿主机随机端口。
- kubernetes.envHostTemplate 必填,且必须包含 {id}。控制面用它生成「打开」链接和 DSH_TRUSTED_HOST。dsh Web 是根路径 SPA,需要每个环境一个主机名(自备通配 DNS),例如 env-{id}.example.com。
- namespace 留空则使用 in-cluster 当前命名空间;集群外可填 kubeconfig。
- ingressClass 可选(例如 nginx)。未填则用集群默认 IngressClass。
- 登记镜像只写目录;kubelet 拉镜像。填写 kubernetes.storageClass 后,/data/dsh 与 /workspace 挂在同一块 PVC 的 home / workspace 子目录;销毁环境时删除 PVC(不保留、不按名复用卷)。未填 storageClass 时退回 emptyDir。
- docker.cpuCores / docker.memoryMB 是容器的 limit。可选的 kubernetes.cpuRequest / kubernetes.memoryRequest 只影响调度预留;不填则 request 等于 limit。
- 控制面 ServiceAccount 需要对本命名空间的 deployments、pods、pods/log、services、secrets、ingresses 有读写权限。
runtime: kubernetes
server:
publicHost: testsuite.example.com
docker:
imageRepository: ghcr.io/cocofhu/dsh-testsuite-runtime
kubernetes:
envHostTemplate: "env-{id}.example.com"
ingressClass: nginx
远程访问
从浏览器访问这台机器的 IP 时:
1. docker.bindIP 设为 0.0.0.0,否则端口只绑在 127.0.0.1,外网会 ERR_EMPTY_RESPONSE / 连不上。
2. server.publicHost 填这台机器的 IP 或域名(也可用 DSHTS_PUBLIC_HOST)。「打开」链接和容器内 dsh --trusted-host 都用它。
3. 上游 dsh 拒绝 --host 0.0.0.0。runtime 让 dsh 听 127.0.0.1:3081,entrypoint 再用 TCP 代理转到容器 3080 给 docker -p。
4. 用 IP + HTTP 打开时浏览器不是 secure context,没有 crypto.randomUUID。镜像会注入 polyfill(与社区 dsh-web-lan-access 同一思路),并把设置 / 凭证 API 放到 --trusted-host 上,否则模型页会报 settings are unavailable in this browser。
5. 0.2.1 起,dsh 会校验启动 URL。进程打印 dsh web: …?token=…,浏览器必须打开这条带 token 的地址才会换到会话 cookie。控制面从容器日志取出 token,把「打开」指到 http://<publicHost>:<端口>/?token=…。镜像还会把该地址作为 --public-url 传给 dsh。
cp config.example.yaml config.yaml
编辑 server.publicHost
export PATH="/usr/local/go/bin:$PATH"
go run ./cmd/dsh-testsuite -config config.yaml
API
GET /healthz
GET /api/providers
GET /api/presets
POST /api/presets { name, provider, model, apiKey, baseURL?, api? }
PUT /api/presets/:id apiKey 留空则不改密钥 DELETE /api/presets/:id GET /api/images GET /api/images/remote 内置可选版本列表 POST /api/images { "version": "0.2.1-rc.4", "ref"?: "...", "pull"?: true } 默认 pull=true;短名会 pull GHCR 再 tag 成本地 imageRepository:version DELETE /api/images/:version POST /api/environments { name, dshVersion, presetId? } 或手动 { apiKey, provider, model, ... } GET /api/environments GET /api/environments/:id POST /api/environments/:id/start POST /api/environments/:id/stop POST /api/environments/:id/restart POST /api/environments/:id/renew 到期时间再延 6 小时 DELETE /api/environments/:id GET /api/environments/:id/logs
MCP
控制面同进程同端口内嵌 MCP(Model Context Protocol)Streamable HTTP 端点,把上表全部能力以 MCP 工具暴露给 AI 智能体:
http://<host>:8090/mcp
支持 initialize / ping / tools/list / tools/call(Streamable HTTP,协议版本 2025-03-26 及以上)。工具面与 REST 完全等价、共用同一份存储——智能体经 MCP 创建的环境立即可在 Web UI / REST 中看到,反之亦然。
| 分类 | 工具 |
| 环境(9) | list_environments create_environment get_environment start_environment stop_environment restart_environment renew_environment(+6h)destroy_environment get_environment_logs(tail 可选) |
| 镜像(4) | list_images list_remote_images register_image(pull 默认 true)delete_image |
| 预设(4) | list_presets create_preset update_preset(apiKey 留空不改)delete_preset |
| 服务商(1) | list_providers |
参数带 JSON Schema 描述,智能体零文档即可发现;not found / conflict / 参数校验等错误都映射为 MCP tool error(isError: true),并保留原始错误文本。密钥规则与 REST 一致:任何工具响应只回显末 4 位。
MCP 客户端配置示例(Claude Code 等 Streamable HTTP 客户端通用):
{
"mcpServers": {
"dsh-testsuite": {
"type": "http",
"url": "http://<host>:8090/mcp"
}
}
}
⚠️ 安全提示:与 REST 控制面一致,/mcp 没有任何认证。绝不要把该端口暴露到公网;请通过内网 / 端口转发访问,否则任何能连到该端口的人都可以读取容器日志、用已存的密钥创建环境,甚至销毁全部环境。
配置
见 config.example.yaml。docker.imageRepository 只在登记时没填 ref 时用来拼默认 仓库:版本。
参与贡献
见 CONTRIBUTING.md。安全问题请按 SECURITY.md 私下报告。
许可证
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
cocofhu/dsh-testsuite
给 DeepSeek Harness 插件准备可复用的在线测试环境。按版本预构建 runtime…
相关插件 · 其他
模型破甲清洗YuJunZhiXue/dsh-purge
⚔️ DeepSeek Harness 网络安全红队工具dsh-purge
★ 16,148已汉化DeepSeek 客户端
73
女娲蒸馏alchaincyf/nuwa-skill
输入名人名字,提炼其思维模型与决策方式供你调用
★ 37,308已汉化DSH
72
逆向分析桥接Fanqi89/dsh-rea
REA(Reverse Engineer Anything)MCP 桥接,用来把 REA 逆向工具接入 DSH 插件
★ 51已汉化DSH
72
语音女友beiyege-01/dsh-voice-ai-girlfriend
语音 AI 女友Voice AI girlfriend for DeepSeek Harness:Whisper…
★ 888已汉化DSH
71.6
红队越狱测试套件Minglink/dsh-infinite-gen-5
DeepSeek v4.1 flash 逆向工具无限五代 求 Star 收藏 ⭐欢迎大家提交项目的改进
★ 3,759已汉化DSH
71.4
协同办公空间dream-num/univer-workspace
一个让人类与 AI Agent 共同创作、协作和审阅的开源 Office 工作空间。
★ 2,266已汉化DSH
70.7