chore: strip monorepo; keep Docker runtime deploy for official dsh@latest
Image installs @deepseek-ai/dsh from npm on start. Fork 0.1.2-alpha.2 source is obsolete for this deployment path.
This commit is contained in:
commit
0bfe0b4a65
11 files changed
+638
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
node_modules
|
||||||
|
*.tsbuildinfo
|
||||||
|
*.map
|
||||||
|
.agent-teams
|
||||||
|
.docker-config
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# OS / editor
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Local workspace mount created by compose if missing
|
||||||
|
workspace/
|
||||||
|
|
||||||
|
# Optional local runtime leftovers
|
||||||
|
coverage/
|
||||||
|
*.tsbuildinfo
|
||||||
|
node_modules/
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
# DeepSeek Harness (DSH) Docker 部署指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本仓库是 DeepSeek Harness 源码 + Docker 构建配置。镜像**只内置工具链**,不把 dsh 钉死在构建层:容器每次启动由 `docker/entrypoint.sh` 从 npm 安装官方 `@deepseek-ai/dsh@latest`(可用环境变量覆盖),安装失败则非零退出,不回退到旧版本。因此**启动时必须能访问 npm registry**。
|
||||||
|
|
||||||
|
### 端口拓扑(当前实现)
|
||||||
|
|
||||||
|
官方 `dsh web` **只监听 `127.0.0.1`**,并明确拒绝 `--host 0.0.0.0`(安全原因:Web 面可远程执行代码)。Docker 的 `-p` 打不到容器内 loopback,故 entrypoint 额外起一层 L4 转发:
|
||||||
|
|
||||||
|
```
|
||||||
|
宿主机 :3080 → 容器 0.0.0.0:3080 → 127.0.0.1:3081 (dsh web --no-open --port 3081)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────┐ http://localhost:3080/?token=… ┌─────────────────────────────┐
|
||||||
|
│ 浏览器 │ ────────────────────────────────▶ │ 0.0.0.0:3080 (转发) │
|
||||||
|
└──────────┘ │ ↓ │
|
||||||
|
│ 127.0.0.1:3081 (dsh web) │
|
||||||
|
└──────────────┬──────────────┘
|
||||||
|
┌──────────────────────────────┼──────────────┐
|
||||||
|
│ dsh-data 卷 │ ./workspace │
|
||||||
|
│ /home/dsh/.dsh │ /workspace │
|
||||||
|
│ (会话/设置/插件/配置) │ (工作目录) │
|
||||||
|
└──────────────────────────────┴──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 浏览器认证(官方包行为)
|
||||||
|
|
||||||
|
官方 `@latest` **强制浏览器认证**,没有免 token 的开关:
|
||||||
|
|
||||||
|
1. 从 `docker logs` 复制带 `?token=` 的 URL,用浏览器打开**一次**
|
||||||
|
2. 服务端 `303` 下发 **30 天签名 cookie**
|
||||||
|
3. 之后同一浏览器直接访问 `http://<host>:3080/` 即可
|
||||||
|
|
||||||
|
**说明**:历史 fork 镜像里的 `DSH_TRUST_ALL=1`、`DSH_TRUSTED_HOSTS` 环境变量、默认绑定 `0.0.0.0` 均**不在官方 npm 包中**。官方侧 Host/Origin 围栏可用 **`--trusted-host`**(compose `command` 传入)扩展非 loopback 权威;cookie 认证仍必须完成。
|
||||||
|
|
||||||
|
## 镜像信息
|
||||||
|
|
||||||
|
**当前 Dockerfile 为「运行时拉取」模型**:构建产出工具链底座(约 747MB,linux/arm64 本地实测),dsh 版本由每次容器启动时的 `@deepseek-ai/dsh@latest` 决定,不体现在镜像 tag 里。
|
||||||
|
|
||||||
|
| 项目 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| 基础镜像 | `registry.cn-hangzhou.aliyuncs.com/skymirror/node:24.19.0-bookworm-slim`(`ARG BASE_IMAGE` 可覆盖;内容同 `docker.io/library/node:24.19.0-bookworm-slim`) |
|
||||||
|
| dsh 安装 | 启动时 `npm install -g @deepseek-ai/dsh@latest`(默认 `registry.npmjs.org`) |
|
||||||
|
| 宿主机端口 | `3080`(compose `3080:3080`) |
|
||||||
|
| 容器内 dsh 端口 | `3081`(仅 `127.0.0.1`) |
|
||||||
|
| ENTRYPOINT | `/usr/local/bin/dsh-entrypoint` |
|
||||||
|
|
||||||
|
### 历史镜像存档(已过时,勿按其行为部署)
|
||||||
|
|
||||||
|
以下 digest 对应**旧 fork 构建时安装**镜像(`0.1.2-alpha.2-skymirror.1`,`dist/` pack + `file:` 闭包、`DSH_TRUST_ALL`、默认 `0.0.0.0`)。当前 Dockerfile 已取代该模型,哈希仅备查:
|
||||||
|
|
||||||
|
| 项目 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| manifest list | `sha256:4c8636d1f20804a39757829de5adb27dccf6435b99610e2a70e74fe8cc95b5bd` |
|
||||||
|
| 更早 monorepo 编译镜像 | `sha256:8a5d6406abd56f5cc3f175aa6b751e226bc91430dfb896add1fcf9a3f970f39e` |
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <仓库地址>
|
||||||
|
cd DSH
|
||||||
|
docker compose build
|
||||||
|
docker compose up -d
|
||||||
|
docker logs -f dsh
|
||||||
|
# 等待出现类似:
|
||||||
|
# dsh-entry: installed 0.1.5-rc.1
|
||||||
|
# dsh-entry: forwarding 0.0.0.0:3080 -> 127.0.0.1:3081
|
||||||
|
# dsh web: http://127.0.0.1:3081/?token=<一次性令牌>
|
||||||
|
# 把日志 URL 中的 127.0.0.1:3081 换成 127.0.0.1:3080(或服务器 IP:3080)后打开
|
||||||
|
```
|
||||||
|
|
||||||
|
首次使用时在 Web UI 中配置模型 API Key(DeepSeek 或任意 OpenAI 兼容端点)。
|
||||||
|
|
||||||
|
国内网络建议在 compose 的 `environment` 里加:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- DSH_NPM_REGISTRY=https://registry.npmmirror.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## 启动环境变量与参数
|
||||||
|
|
||||||
|
### 环境变量(`docker/entrypoint.sh`)
|
||||||
|
|
||||||
|
| 变量 | 默认 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `DSH_PACKAGE` | `@deepseek-ai/dsh` | 要安装的包名 |
|
||||||
|
| `DSH_DIST_TAG` | `latest` | npm dist-tag 或精确版本(如 `0.1.5-rc.1`) |
|
||||||
|
| `DSH_NPM_REGISTRY` | `https://registry.npmjs.org` | 拉包 registry;国内可设 `https://registry.npmmirror.com` |
|
||||||
|
| `DSH_NPM_CACHE` | `$DSH_HOME/npm-cache` | npm cache(`dsh-data` 卷内,重启可命中) |
|
||||||
|
| `DSH_INTERNAL_PORT` | `3081` | dsh 监听端口(仅 `127.0.0.1`) |
|
||||||
|
| `DSH_FORWARD_PORT` | `3080` | entrypoint 在 `0.0.0.0` 上的转发端口(Docker `-p` 应映射此端口) |
|
||||||
|
|
||||||
|
### 传给 `dsh web` 的参数
|
||||||
|
|
||||||
|
entrypoint 固定附加 `--no-open --port ${DSH_INTERNAL_PORT}`。compose 的 `command` 会**追加**在后面,例如:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
command: ["--trusted-host", "203.0.113.10"]
|
||||||
|
```
|
||||||
|
|
||||||
|
| 参数 | 作用 |
|
||||||
|
|------|------|
|
||||||
|
| `--trusted-host <authority>` | 向 Host/Origin 围栏追加非 loopback 权威(可重复);**不**替代 cookie 认证 |
|
||||||
|
| `--port` / `--host` | 不要通过 command 覆盖:端口由 `DSH_INTERNAL_PORT` 管理;`--host 0.0.0.0` 会被官方拒绝 |
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
- Docker Engine 20.10+(BuildKit)
|
||||||
|
- Docker Compose v2
|
||||||
|
- 启动时能访问 npm registry(官方 npmjs 或 npmmirror)
|
||||||
|
|
||||||
|
## 一、不用 Docker 直接跑
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Node.js ^22.19 || >=24
|
||||||
|
npx --yes @deepseek-ai/dsh@latest web
|
||||||
|
# 或
|
||||||
|
npm install -g @deepseek-ai/dsh@latest
|
||||||
|
dsh web --no-open --port 3080
|
||||||
|
```
|
||||||
|
|
||||||
|
本机默认只监听 `127.0.0.1`,浏览器打开日志里的 `?token=` URL。管理插件需要 `corepack enable pnpm`。
|
||||||
|
|
||||||
|
查看可用版本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm view @deepseek-ai/dsh dist-tags
|
||||||
|
```
|
||||||
|
|
||||||
|
### npm 路径 vs Docker
|
||||||
|
|
||||||
|
| | `npx @latest` | Docker(本仓库) |
|
||||||
|
|---|---|---|
|
||||||
|
| 启动耗时 | 首次拉包约 15s–数分钟(视网络/cache) | 镜像已有工具链;每次启动装 dsh(有 cache 更快) |
|
||||||
|
| 构建 | 无 | 只装 apt/工具链,不编译 monorepo |
|
||||||
|
| 工具链 | 仅本机 Node | Python / gcc·g++ / pnpm / Rust 按需 |
|
||||||
|
| 升级 | 每次 `npx@latest` | 每次容器启动装 `@latest`(失败退出) |
|
||||||
|
| 数据 | `$DSH_HOME`(默认 `~/.dsh`) | `dsh-data` → `/home/dsh/.dsh` |
|
||||||
|
| 远程端口 | 只听 loopback | entrypoint `3080` → dsh `3081` |
|
||||||
|
|
||||||
|
## 二、本地构建并运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose build
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
构建不需要 `pnpm build`、`release:pack` 或 `dist/`。
|
||||||
|
|
||||||
|
### 基础镜像拉不动时
|
||||||
|
|
||||||
|
Docker Hub 在部分网络不可达。可先把官方 node 镜像同步到自己 registry,再:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker buildx build \
|
||||||
|
--build-arg BASE_IMAGE=registry.cn-hangzhou.aliyuncs.com/skymirror/node:24.19.0-bookworm-slim \
|
||||||
|
--platform linux/arm64 \
|
||||||
|
-t dsh:local-arm64 --load .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 清理 buildx 缓存
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker buildx du
|
||||||
|
docker buildx prune --filter until=24h
|
||||||
|
# 或
|
||||||
|
docker buildx prune --all
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、云效构建并推送
|
||||||
|
|
||||||
|
流水线「镜像构建」使用仓库根目录 `Dockerfile`,推送到 ACR。目标机:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
|
||||||
|
docker run -d --name dsh \
|
||||||
|
-p 3080:3080 \
|
||||||
|
-v dsh-data:/home/dsh/.dsh \
|
||||||
|
-v /path/to/workspace:/workspace \
|
||||||
|
-e DSH_NPM_REGISTRY=https://registry.npmmirror.com \
|
||||||
|
registry.cn-hangzhou.aliyuncs.com/skymirror/dsh:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:推上去的是**工具链底座**;运行时仍会拉 npm 上的 dsh。请保证部署机可访问所配置的 registry。
|
||||||
|
|
||||||
|
## 四、目录 / 端口映射
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 宿主机 `3080` → 容器 `3080` | entrypoint 的 `0.0.0.0` 转发口 |
|
||||||
|
| 容器内 dsh `3081` | 仅 `127.0.0.1`,不要单独 `-p` 映射 |
|
||||||
|
| `dsh-data` → `/home/dsh/.dsh` | 会话、settings、storages、profiles(插件)、npm-cache |
|
||||||
|
| `./workspace` → `/workspace` | agent 工作目录;目录选择器默认起点(`HOME`) |
|
||||||
|
| `$DSH_HOME/profiles/<name>` | 插件与 profile,卷内**持久化** |
|
||||||
|
| `dsh` 命令 | 启动时 `npm install -g` 装入 PATH,供 `docker exec` 管理插件 |
|
||||||
|
|
||||||
|
插件示例:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it dsh dsh plugin --profile web add <package>
|
||||||
|
# 改完插件需重启容器
|
||||||
|
docker restart dsh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、镜像内置语言环境
|
||||||
|
|
||||||
|
| 语言/工具 | 版本 | 说明 |
|
||||||
|
|-----------|------|------|
|
||||||
|
| Node.js | 24.19.0 | 基础镜像 |
|
||||||
|
| npm | 11.17.0 | 全局升级 |
|
||||||
|
| pnpm | 11.7.0 | corepack;`dsh plugin add` 使用 |
|
||||||
|
| Python | 3.11 | apt,含 pip/venv/Python.h |
|
||||||
|
| Rust | stable 按需 | 首次调用 `cargo`/`rustc`/`rustup` 时装进 `dsh-data` 卷 |
|
||||||
|
| gcc / g++ / make | bookworm | 原生模块编译 |
|
||||||
|
| git / curl | apt | |
|
||||||
|
|
||||||
|
不内置 JDK、Go(体积取舍)。需要时进容器自行安装。
|
||||||
|
|
||||||
|
> 构建默认走国内源:npm `npmmirror`、Debian 清华、rustup USTC、pip 清华。云效等可出网环境可用 `--build-arg NPM_REGISTRY=...` 等覆盖。
|
||||||
|
|
||||||
|
### Rust 按需安装
|
||||||
|
|
||||||
|
`/usr/local/bin/ensure-rust` 包装器在首次调用时把 toolchain 装进 `$RUSTUP_HOME` / `$CARGO_HOME`(均在 `dsh-data` 卷)。可提前 `docker exec dsh cargo --version` 触发。
|
||||||
|
|
||||||
|
## 六、自定义配置
|
||||||
|
|
||||||
|
### 修改端口
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- DSH_FORWARD_PORT=8080 # 容器 0.0.0.0 转发口
|
||||||
|
- DSH_INTERNAL_PORT=3081 # dsh 本机端口(一般不用改)
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 从其它机器访问
|
||||||
|
|
||||||
|
1. **Host/Origin 围栏**:浏览器地址为 `http://<服务器IP>:3080` 时,Host 不是 loopback,需在 compose 中追加:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
command: ["--trusted-host", "<服务器IP或域名>"]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **浏览器认证**:仍必须用日志里的 `?token=` URL 打开一次,换 30 天 cookie。
|
||||||
|
|
||||||
|
3. **传输与入口**:DSH 可执行代码。公网暴露请在前面加带认证/TLS 的反向代理,不要裸奔。
|
||||||
|
|
||||||
|
### 挂载自己的数据目录
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- /data/dsh:/home/dsh/.dsh
|
||||||
|
- /data/work:/workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
## 七、常见问题
|
||||||
|
|
||||||
|
| 现象 | 处理 |
|
||||||
|
|------|------|
|
||||||
|
| 启动很慢或退出 | 看 `docker logs`:多半是装 `@latest` 网络失败;设 `DSH_NPM_REGISTRY=https://registry.npmmirror.com` |
|
||||||
|
| `authentication required; reopen the URL printed by dsh web` | 正常:用带 `?token=` 的 URL 打开一次;官方包**不能**免 token |
|
||||||
|
| `GET /` 401 | 同上 |
|
||||||
|
| `/api` 403 | Host 不在围栏内:给 `dsh web` 加 `--trusted-host <你的Host>` |
|
||||||
|
| 宿主机 3080 连不上 / connection reset | 确认映射的是转发口 `3080:3080`,且 logs 有 `forwarding 0.0.0.0:3080 -> 127.0.0.1:3081` |
|
||||||
|
| `--host 0.0.0.0 is intentionally not supported` | 官方限制;不要传 `--host 0.0.0.0`,依赖 entrypoint 转发 |
|
||||||
|
| 端口被占用 | 改 compose 的 `ports` 与 `DSH_FORWARD_PORT` |
|
||||||
|
| `Cannot find module .../dsh/lib/bin.js` | 自定义了 `entrypoint:`/`command:` 绕过安装入口;去掉覆盖后重建 |
|
||||||
|
| 插件重启后消失 | 插件应装在 `$DSH_HOME/profiles`(卷内);确认未挂错卷 |
|
||||||
|
| 设置了 `DSH_TRUST_ALL=1` 仍要 token | **预期行为**:官方包无此变量;历史 fork 镜像才有 |
|
||||||
|
|
||||||
|
## 附录:fork 私有 npm 发布(可选,当前 Docker 不用)
|
||||||
|
|
||||||
|
当前 Docker 路径**只消费官方 npm `@latest`**。若仍需维护 skymirror fork 并发到私有 registry,可继续用仓库内 release 脚本(与镜像构建解耦):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm run release:dsh <version>
|
||||||
|
pnpm run build:official
|
||||||
|
pnpm run release:pack --family dsh --out dist/npm
|
||||||
|
# … vendor / landlock / verify / publish,见 scripts/release/
|
||||||
|
```
|
||||||
|
|
||||||
|
fork 的 `DSH_TRUST_ALL`、`DSH_TRUSTED_HOSTS` 环境变量、默认 `0.0.0.0` 绑定等部署补丁只存在于 fork 编译产物中;要在容器里用它们,需把 `DSH_PACKAGE` / `DSH_DIST_TAG` / `DSH_NPM_REGISTRY` 指到私有 registry 的 fork 包,并自行承担与官方 `@latest` 的行为差异。
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
# ── DeepSeek Harness — 运行时拉取最新版镜像 ──────────────────────────────────
|
||||||
|
# 镜像只装运行时工具链(Node/Python/gcc/pnpm/Rust 引导),不把 dsh 钉死在
|
||||||
|
# 构建层。容器每次启动由 docker/entrypoint.sh 从 npm 安装
|
||||||
|
# @deepseek-ai/dsh@latest(可用 DSH_DIST_TAG / DSH_NPM_REGISTRY 覆盖),
|
||||||
|
# 安装失败直接退出,不回退到镜像内置版本。
|
||||||
|
# 构建不再需要本地 dist/ pack 产物。
|
||||||
|
#
|
||||||
|
# npm registry / apt 源可通过 --build-arg 覆盖;默认走国内镜像
|
||||||
|
# (本机/国内构建环境通常无法直连官方源)。
|
||||||
|
ARG NPM_REGISTRY=https://registry.npmmirror.com
|
||||||
|
# Debian 软件源可通过 --build-arg APT_MIRROR=... 覆盖;默认走清华镜像
|
||||||
|
# (deb.debian.org 在国内网络极慢)。
|
||||||
|
ARG APT_MIRROR=mirrors.tuna.tsinghua.edu.cn
|
||||||
|
# 基础镜像:默认 ACR 上的 node(由 docker.io/library/node:24.19.0-bookworm-slim 同步)
|
||||||
|
ARG BASE_IMAGE=registry.cn-hangzhou.aliyuncs.com/skymirror/node:24.19.0-bookworm-slim
|
||||||
|
|
||||||
|
FROM ${BASE_IMAGE}
|
||||||
|
ARG NPM_REGISTRY
|
||||||
|
ARG APT_MIRROR
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
ENV NPM_CONFIG_REGISTRY=${NPM_REGISTRY} \
|
||||||
|
COREPACK_NPM_REGISTRY=${NPM_REGISTRY} \
|
||||||
|
NODEJS_ORG_MIRROR=https://npmmirror.com/mirrors/node \
|
||||||
|
npm_config_userconfig=/home/dsh/.dsh/.npmrc
|
||||||
|
|
||||||
|
# 切换到国内 Debian 镜像(bookworm 的 deb822 源),否则 apt 下载极慢
|
||||||
|
RUN sed -i "s|deb.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list.d/debian.sources
|
||||||
|
|
||||||
|
# 基础工具 + node-gyp/插件编译链(build-essential + python3)+ Python 3.11(apt 预编译,glibc 一致)。
|
||||||
|
# python3-dev 提供 Python.h,供 agent 编译带 C 扩展的 Python 包。
|
||||||
|
# 工具链保留(npm 路径镜像):koffi 优先用包内预编译平台二进制,landlock 等无预编译的
|
||||||
|
# 原生件在安装期按 arch 编译。JDK/Go 不再内置(旧全量镜像的能力,npm 路径按需安装)。
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
curl ca-certificates git xz-utils \
|
||||||
|
build-essential \
|
||||||
|
python3 python3-dev python3-pip python3-venv \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# npm 精确版本 + pnpm(`dsh plugin add` 内部走 pnpm,allowBuilds 预置在安装期生效)
|
||||||
|
RUN npm install -g npm@11.17.0 \
|
||||||
|
&& corepack enable \
|
||||||
|
&& corepack prepare pnpm@11.7.0 --activate
|
||||||
|
|
||||||
|
# Rust 按需安装:只内置 rustup-init 引导程序(几 MB),首次调用 cargo/rustc/rustup 时
|
||||||
|
# 由 /usr/local/bin/ensure-rust 把最小 stable toolchain 装进 dsh-data 持久卷,镜像省下 ~1GB。
|
||||||
|
RUN case "$TARGETARCH" in \
|
||||||
|
amd64) RUSTUP_TRIPLE=x86_64-unknown-linux-gnu ;; \
|
||||||
|
arm64) RUSTUP_TRIPLE=aarch64-unknown-linux-gnu ;; \
|
||||||
|
*) echo "unsupported arch: $TARGETARCH" >&2; exit 1 ;; \
|
||||||
|
esac \
|
||||||
|
&& (curl -fsSL "https://mirrors.ustc.edu.cn/rust-static/rustup/dist/${RUSTUP_TRIPLE}/rustup-init" -o /usr/local/bin/rustup-init \
|
||||||
|
|| curl -fsSL "https://rsproxy.cn/rustup/dist/${RUSTUP_TRIPLE}/rustup-init" -o /usr/local/bin/rustup-init) \
|
||||||
|
&& chmod +x /usr/local/bin/rustup-init
|
||||||
|
|
||||||
|
# Rust toolchain 下载走 USTC 镜像(清华 rustup 镜像已下线该路径);toolchain 与 cargo 家目录落在 dsh-data 卷内(重启后保留)
|
||||||
|
ENV RUSTUP_HOME=/home/dsh/.dsh/rustup \
|
||||||
|
CARGO_HOME=/home/dsh/.dsh/cargo \
|
||||||
|
RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static \
|
||||||
|
RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
|
||||||
|
|
||||||
|
# cargo 默认走清华 crates 稀疏索引(写入持久卷,首次挂载空卷时随镜像复制进去)
|
||||||
|
RUN mkdir -p /home/dsh/.dsh/cargo \
|
||||||
|
&& printf '%s\n' \
|
||||||
|
'[source.crates-io]' \
|
||||||
|
'replace-with = "tuna"' \
|
||||||
|
'[source.tuna]' \
|
||||||
|
'registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"' \
|
||||||
|
> /home/dsh/.dsh/cargo/config.toml
|
||||||
|
|
||||||
|
# Rust 包装器 + python 软链接(python/pip 与 python3/pip3 同名可互换)
|
||||||
|
COPY docker/ensure-rust.sh /usr/local/bin/ensure-rust
|
||||||
|
RUN chmod +x /usr/local/bin/ensure-rust \
|
||||||
|
&& ln -sf /usr/local/bin/ensure-rust /usr/local/bin/cargo \
|
||||||
|
&& ln -sf /usr/local/bin/ensure-rust /usr/local/bin/rustc \
|
||||||
|
&& ln -sf /usr/local/bin/ensure-rust /usr/local/bin/rustup \
|
||||||
|
&& ln -sf /usr/bin/python3 /usr/local/bin/python \
|
||||||
|
&& ln -sf /usr/bin/pip3 /usr/local/bin/pip
|
||||||
|
|
||||||
|
# DSH 数据目录(会话/设置/配置/插件)与 agent 工作目录
|
||||||
|
# 写一份 npmrc 让运行时 npm / `dsh plugin add`(内部走 pnpm)默认用同一镜像;
|
||||||
|
# 放在 dsh-data 卷内,首次挂载空卷时 Docker 会复制进去,之后仍可被用户覆盖。
|
||||||
|
# 运行时 ENV 的 npm_config_userconfig 指向这里(HOME=/workspace 使 /root/.npmrc 不生效)。
|
||||||
|
RUN mkdir -p /home/dsh/.dsh /workspace \
|
||||||
|
&& printf "registry=%s\n" "${NPM_REGISTRY}" > /home/dsh/.dsh/.npmrc \
|
||||||
|
&& printf "registry=%s\n" "${NPM_REGISTRY}" > /root/.npmrc
|
||||||
|
|
||||||
|
# GOROOT/GOPATH/JAVA_HOME 已随 JDK/Go 移除;PATH 恢复标准顺序(含 /usr/local/bin 供 dsh 等软链)
|
||||||
|
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
|
||||||
|
# pip 默认走清华镜像(供 agent 在 venv 里 pip install)
|
||||||
|
ENV PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
|
||||||
|
ENV DSH_HOME=/home/dsh/.dsh
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
# 让目录选择器默认起始目录为 /workspace(os.homedir() 读 HOME)
|
||||||
|
ENV HOME=/workspace
|
||||||
|
# pnpm 全局目录放进 dsh-data 卷,避免 HOME=/workspace 未挂载时全局包/状态丢失
|
||||||
|
ENV PNPM_HOME=/home/dsh/.dsh/pnpm
|
||||||
|
|
||||||
|
# 3081 = dsh 内部 127.0.0.1;3080 = entrypoint 的 0.0.0.0 转发(Docker -p 映射 3080)
|
||||||
|
EXPOSE 3080
|
||||||
|
|
||||||
|
# 启动时安装最新版 dsh,起 3080→127.0.0.1:3081 转发后 exec web;失败则非零退出
|
||||||
|
# (见 docker/entrypoint.sh)
|
||||||
|
COPY docker/entrypoint.sh /usr/local/bin/dsh-entrypoint
|
||||||
|
RUN chmod +x /usr/local/bin/dsh-entrypoint
|
||||||
|
ENTRYPOINT ["/usr/local/bin/dsh-entrypoint"]
|
||||||
|
# 额外参数追加给 dsh web;--no-open 与端口由 entrypoint 固定(内部 3081 / 转发 3080)
|
||||||
|
CMD []
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 DeepSeek
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# DeepSeek Harness Docker Deploy
|
||||||
|
|
||||||
|
Runtime image for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) Web UI.
|
||||||
|
|
||||||
|
The image ships a toolchain only. On every container start, `docker/entrypoint.sh` installs official `@deepseek-ai/dsh@latest` from npm, then starts `dsh web` behind a local port forward (`0.0.0.0:3080` → `127.0.0.1:3081`).
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose build
|
||||||
|
docker compose up -d
|
||||||
|
docker logs -f dsh
|
||||||
|
# Open the printed URL with ?token= once (30-day cookie). Use host port 3080.
|
||||||
|
```
|
||||||
|
|
||||||
|
Full guide: [DEPLOY.md](DEPLOY.md)
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
| Path | Role |
|
||||||
|
|------|------|
|
||||||
|
| `Dockerfile` | Toolchain image (Node 24 / Python 3.11 / gcc / pnpm / rustup bootstrap) |
|
||||||
|
| `docker-compose.yml` | Ports, `dsh-data` volume, workspace mount |
|
||||||
|
| `docker/entrypoint.sh` | Install `@deepseek-ai/dsh@latest` and start web |
|
||||||
|
| `docker/ensure-rust.sh` | On-demand Rust toolchain installer |
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT — see [LICENSE](LICENSE). Upstream DSH is MIT; this repo is a deployment wrapper.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# DeepSeek Harness Docker 部署
|
||||||
|
|
||||||
|
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)Web UI 的运行时镜像。
|
||||||
|
|
||||||
|
镜像只内置工具链。容器每次启动由 `docker/entrypoint.sh` 从 npm 安装官方 `@deepseek-ai/dsh@latest`,再经本地端口转发(`0.0.0.0:3080` → `127.0.0.1:3081`)启动 `dsh web`。
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose build
|
||||||
|
docker compose up -d
|
||||||
|
docker logs -f dsh
|
||||||
|
# 用日志里带 ?token= 的 URL 打开一次(30 天 cookie)。宿主机访问 3080。
|
||||||
|
```
|
||||||
|
|
||||||
|
完整说明:[DEPLOY.md](DEPLOY.md)
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
| 路径 | 作用 |
|
||||||
|
|------|------|
|
||||||
|
| `Dockerfile` | 工具链镜像(Node 24 / Python 3.11 / gcc / pnpm / rustup 引导) |
|
||||||
|
| `docker-compose.yml` | 端口、`dsh-data` 卷、工作区挂载 |
|
||||||
|
| `docker/entrypoint.sh` | 安装 `@deepseek-ai/dsh@latest` 并启动 web |
|
||||||
|
| `docker/ensure-rust.sh` | Rust 按需安装包装器 |
|
||||||
|
|
||||||
|
## 许可
|
||||||
|
|
||||||
|
MIT — 见 [LICENSE](LICENSE)。上游 DSH 为 MIT;本仓库是部署封装。
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Safety
|
||||||
|
|
||||||
|
English | [中文](SAFETY.zh.md)
|
||||||
|
|
||||||
|
## Experimental status
|
||||||
|
|
||||||
|
DeepSeek Harness is experimental developer-preview software. It has not undergone a security audit and must not be treated as secure or production-ready.
|
||||||
|
|
||||||
|
The project can execute model-generated code and commands, load third-party plugins, and access the network, processes, credentials, and files made available to it. Incorrect model output, defects, misconfiguration, malicious input, or untrusted plugins may damage the host computer, modify or delete files, disclose data or credentials, or cause other unintended effects.
|
||||||
|
|
||||||
|
## Sandbox limitations
|
||||||
|
|
||||||
|
Sandboxing, approval prompts, and permission controls can reduce risk, but they do not guarantee isolation or prevent damage. Even correctly enforced restrictions cannot protect resources that the project is allowed to access.
|
||||||
|
|
||||||
|
Do not rely on DeepSeek Harness as the sole security control for untrusted workloads.
|
||||||
|
|
||||||
|
## Responsible use
|
||||||
|
|
||||||
|
- Run the project with the least privileges and access required.
|
||||||
|
- Prefer a disposable virtual machine, container, or dedicated environment.
|
||||||
|
- Keep backups of files that the project can access.
|
||||||
|
- Do not expose sensitive credentials or data unless you accept the risk.
|
||||||
|
- Review plugins, configuration, and proposed commands before allowing them to run.
|
||||||
|
|
||||||
|
## No warranty or liability
|
||||||
|
|
||||||
|
Use DeepSeek Harness at your own risk. The software is provided without warranty under the [MIT License](LICENSE). To the maximum extent permitted by applicable law, the authors and copyright holders are not responsible for damage to computers, loss or disclosure of data, loss of files, or other harm arising from use of the project.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
services:
|
||||||
|
dsh:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
ports:
|
||||||
|
# 官方 dsh 只听 127.0.0.1:3081;entrypoint 在 0.0.0.0:3080 做 L4 转发
|
||||||
|
- "3080:3080"
|
||||||
|
# 需要向 Host/Origin 围栏声明非 loopback 权威时追加(不替代 token cookie):
|
||||||
|
# command: ["--trusted-host", "your-server-ip-or-domain"]
|
||||||
|
volumes:
|
||||||
|
- dsh-data:/home/dsh/.dsh # 会话、设置、配置、profiles(插件)持久化
|
||||||
|
- ./workspace:/workspace # agent 的工作目录
|
||||||
|
environment:
|
||||||
|
- DSH_HOME=/home/dsh/.dsh
|
||||||
|
- NODE_ENV=production
|
||||||
|
# 目录选择器默认从 /workspace 开始(HOME 决定 os.homedir())
|
||||||
|
- HOME=/workspace
|
||||||
|
# 启动时安装的 dsh(默认官方 npmjs 的 latest;失败则容器退出)
|
||||||
|
# - DSH_DIST_TAG=latest
|
||||||
|
# - DSH_NPM_REGISTRY=https://registry.npmmirror.com
|
||||||
|
# - DSH_PACKAGE=@deepseek-ai/dsh
|
||||||
|
# - DSH_INTERNAL_PORT=3081
|
||||||
|
# - DSH_FORWARD_PORT=3080
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
dsh-data:
|
||||||
Executable
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Rust 按需安装包装器:镜像里 /usr/local/bin/{cargo,rustc,rustup} 都是指向本脚本的软链接。
|
||||||
|
# 首次调用任一命令时,用 rustup-init 把最小 stable toolchain 装进 dsh-data 持久卷
|
||||||
|
# (RUSTUP_HOME/CARGO_HOME 已在镜像 ENV 中指向 /home/dsh/.dsh/...),之后直接转发到真实二进制。
|
||||||
|
# toolchain 通过 RUSTUP_DIST_SERVER(USTC 镜像)下载,重启容器后已装的 toolchain 仍保留。
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
: "${RUSTUP_HOME:=/home/dsh/.dsh/rustup}"
|
||||||
|
: "${CARGO_HOME:=/home/dsh/.dsh/cargo}"
|
||||||
|
export RUSTUP_HOME CARGO_HOME
|
||||||
|
|
||||||
|
cmd="$(basename "$0")"
|
||||||
|
target="${CARGO_HOME}/bin/${cmd}"
|
||||||
|
|
||||||
|
if [ ! -x "$target" ]; then
|
||||||
|
lockdir="${RUSTUP_HOME}/.installing"
|
||||||
|
mine=0
|
||||||
|
# 并发首次调用只允许一个进程执行 rustup-init,其余进程等待它完成。
|
||||||
|
while [ "$mine" -eq 0 ]; do
|
||||||
|
if mkdir "$lockdir" 2>/dev/null; then
|
||||||
|
mine=1
|
||||||
|
elif [ -x "$target" ]; then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ ! -x "$target" ]; then
|
||||||
|
rustup-init -y --profile minimal --default-toolchain stable --no-modify-path >&2
|
||||||
|
fi
|
||||||
|
if [ "$mine" -eq 1 ]; then
|
||||||
|
rmdir "$lockdir" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [ ! -x "$target" ]; then
|
||||||
|
echo "ensure-rust: rustup-init failed to install ${cmd}; check network and retry." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$target" "$@"
|
||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# 容器入口:启动时从 npm 安装指定 dist-tag 的 dsh,失败则退出(无镜像内置兜底)。
|
||||||
|
# 官方 dsh web 只绑定 127.0.0.1 且拒绝 --host 0.0.0.0,Docker -p 打不到;
|
||||||
|
# 故在 0.0.0.0:3080 起一层 L4 转发到 127.0.0.1:3081,宿主机映射容器 3080 即可访问。
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
DSH_PACKAGE="${DSH_PACKAGE:-@deepseek-ai/dsh}"
|
||||||
|
DSH_DIST_TAG="${DSH_DIST_TAG:-latest}"
|
||||||
|
# 默认官方 npmjs;国内网络可设 DSH_NPM_REGISTRY=https://registry.npmmirror.com
|
||||||
|
DSH_NPM_REGISTRY="${DSH_NPM_REGISTRY:-https://registry.npmjs.org}"
|
||||||
|
# 安装缓存放进 dsh-data 卷,重启后命中缓存,缩短二次启动
|
||||||
|
DSH_NPM_CACHE="${DSH_NPM_CACHE:-${DSH_HOME:-/home/dsh/.dsh}/npm-cache}"
|
||||||
|
# dsh 本机监听端口(官方拒绝 --host 0.0.0.0,只听 127.0.0.1)
|
||||||
|
DSH_INTERNAL_PORT="${DSH_INTERNAL_PORT:-3081}"
|
||||||
|
# 对 Docker 暴露的转发端口(宿主机 -p 映射这一侧);3080 → 127.0.0.1:3081
|
||||||
|
DSH_FORWARD_PORT="${DSH_FORWARD_PORT:-3080}"
|
||||||
|
|
||||||
|
echo "dsh-entry: installing ${DSH_PACKAGE}@${DSH_DIST_TAG} from ${DSH_NPM_REGISTRY}" >&2
|
||||||
|
|
||||||
|
npm install -g "${DSH_PACKAGE}@${DSH_DIST_TAG}" \
|
||||||
|
--registry "${DSH_NPM_REGISTRY}" \
|
||||||
|
--no-audit \
|
||||||
|
--no-fund \
|
||||||
|
--cache "${DSH_NPM_CACHE}"
|
||||||
|
|
||||||
|
echo "dsh-entry: installed $(dsh --version)" >&2
|
||||||
|
|
||||||
|
export DSH_INTERNAL_PORT DSH_FORWARD_PORT
|
||||||
|
node -e '
|
||||||
|
const net = require("node:net");
|
||||||
|
const targetPort = Number(process.env.DSH_INTERNAL_PORT || 3081);
|
||||||
|
const listenPort = Number(process.env.DSH_FORWARD_PORT || 3080);
|
||||||
|
const server = net.createServer((client) => {
|
||||||
|
const upstream = net.connect({ host: "127.0.0.1", port: targetPort });
|
||||||
|
const kill = () => {
|
||||||
|
client.destroy();
|
||||||
|
upstream.destroy();
|
||||||
|
};
|
||||||
|
client.on("error", kill);
|
||||||
|
upstream.on("error", kill);
|
||||||
|
client.pipe(upstream);
|
||||||
|
upstream.pipe(client);
|
||||||
|
});
|
||||||
|
server.on("error", (err) => {
|
||||||
|
console.error(`dsh-entry: forwarder failed: ${err.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
server.listen(listenPort, "0.0.0.0", () => {
|
||||||
|
console.error(`dsh-entry: forwarding 0.0.0.0:${listenPort} -> 127.0.0.1:${targetPort}`);
|
||||||
|
});
|
||||||
|
' &
|
||||||
|
|
||||||
|
# --no-open:容器内没有浏览器;端口固定为 DSH_INTERNAL_PORT
|
||||||
|
exec dsh web --no-open --port "${DSH_INTERNAL_PORT}" "$@"
|
||||||
Reference in new issue
Block a user