为 DeepSeek Harness(dsh)加固的 Docker 打包方案——独立项目,与 DeepSeek 无关
30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/21 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
该插件未发布到 npm,按作者文档从 GitHub 源安装即可(静态检查只覆盖 npm 路径,不代表插件不可用)。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add sismotur/dsh-docker该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 11 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(unknown · 2026/10/8) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装仓库 package.json 标记 private,未发布到 npm,需从源码安装;安装清单取自 plugins/dsh-git-branch-dock/package.json
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-git-branch-dock(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/11 22:21:59
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 4 处,展示前 4 条)
- 读取本地文件list-sessions.js:22
if (!fs.existsSync(path)) return []; - 读取本地文件list-sessions.js:25
j = JSON.parse(fs.readFileSync(path, "utf8")); - 发起网络请求plugins/dsh-git-branch-dock/lib/client.js:61
return fetch(ENDPOINT, { method: "GET", credentials: "same-origin" }) - 读取本地文件plugins/dsh-git-branch-dock/lib/index.js:23
await access(p);
扫描于 2026/10/31(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 只读容器隔离运行
- Docker打包方案
README
由 DeepSeek 最新模型翻译生成dsh — 加固版 Docker 部署
在锁定过的容器里运行 DeepSeek harness 命令行(@deepseek-ai/dsh),不再直接跑在宿主机上。
与 DeepSeek Harness 的关系。 本仓库把 @deepseek-ai/dsh 打包进 Docker 并做加固。
它不是 DeepSeek 官方产品,也不是 deepseek-ai/deepseek-harness 的 git fork。
上游采用 MIT 协议;见 LICENSE(本项目)和 NOTICE.md(第三方署名)。
锁定的 dsh 版本: @deepseek-ai/dsh@0.2.0-rc.2(见 global-tools/package.json 和 lockfile)。上游属于开发者预览,可能发布破坏兼容性的改动 —— 本镜像锁定一个已知 RC,不跟随 latest。文档: deepseek-harness documentation。
人类快速指南: 安装步骤、日常 ./run-dsh.sh 命令、如何测试整套环境,见 USAGE.md。
展开完整文档(README 剩余 1 节)
dsh — 加固版 Docker 部署
在锁定过的容器里运行 DeepSeek harness 命令行(@deepseek-ai/dsh),不再直接跑在宿主机上。
与 DeepSeek Harness 的关系。 本仓库把 @deepseek-ai/dsh 打包进 Docker 并做加固。
它不是 DeepSeek 官方产品,也不是 deepseek-ai/deepseek-harness 的 git fork。
上游采用 MIT 协议;见 LICENSE(本项目)和 NOTICE.md(第三方署名)。
锁定的 dsh 版本: @deepseek-ai/dsh@0.2.0-rc.2(见 global-tools/package.json 和 lockfile)。上游属于开发者预览,可能发布破坏兼容性的改动 —— 本镜像锁定一个已知 RC,不跟随 latest。文档: deepseek-harness documentation。
人类快速指南: 安装步骤、日常 ./run-dsh.sh 命令、如何测试整套环境,见 USAGE.md。
目录
- 为什么
- 加固配置
- 权限模型与故障安全默认值
- 前置条件
- 部署
- 启动与停止
- Web 配置档
- 无头配置档
- 后台无头任务
- 取消后台任务
- 查看运行中的内容
- 首次运行就绪检查
- 在容器内调试
- 清理
- 会话历史
- 运行日志
- 任务模板
- 在已有项目上工作
- 项目别名
- Git 身份
- 安全权衡
- 插件
- MCP 服务器
- LiteLLM 路由
- oMLX 配置
- 安全审计
- 安全不变量
- 验证加固
- 使用指南
- 协议与署名
- 文件
为什么
裸机上的 dsh 是完整的智能体 harness:它能执行任意 bash、读写文件、浏览网页,还能以你的用户权限启动子代理。本方案把它关进一个非 root、剥离 capabilities、根文件系统只读的容器,只暴露一个工作区目录。
加固配置
注意(dsh ≥ 0.2): 原生 Node 插件不能缓存在 noexec 的 /tmp 下。本 compose 设置 NARB_DISABLE_NATIVE_CACHE=1,让预构建产物从 /usr/local 加载。
安装缓存: pnpm/npm 的临时文件使用 TMPDIR=/data/tmp 和 npm_config_cache=/data/.npm,两者都放在 dsh-home 卷上。64 MB 的 /tmp tmpfs 保持很小,并保持 noexec 以加固安全。
下面每一项控制都设在 docker-compose.yml(运行时)或 Dockerfile(构建时)。已验证的效果是验证过程中实际观察到的结果。
运行时控制(docker-compose.yml)
- 非 root 用户 — user: "1000:1000"。容器以 node 用户运行(uid 1000,由 node:25-slim 提供),绝不用 root。已验证:id 输出 uid=1000(node)。
- 丢弃全部能力 — cap_drop: [ALL]。移除所有 Linux 能力(如 CAP_SYS_ADMIN、CAP_NET_ADMIN)。已验证:CapEff: 0000000000000000(有效能力为零)。
- 禁止提权 — security_opt: [no-new-privileges:true]。阻止 setuid 二进制提升权限。
- 只读根文件系统 — read_only: true。容器 rootfs 以只读方式挂载。已验证:写入 / 和 /etc 会失败,报 "Read-only file system"。只有三个路径可写:
- /tmp — tmpfs(noexec,nosuid,64 MB),每个容器临时独立。
- /data — 命名卷 dsh-home,持久保存 DSH_HOME 状态。
- /workspace — 绑定挂载到 ~/dsh-docker/workspace,这是智能体唯一能碰到的宿主机目录。
- 限定卷范围 — 只绑定挂载 ./workspace。不暴露 ~、~/.ssh、~/.aws、~/.config 或浏览器目录。
- 资源限制 — cpus: "4"、mem_limit: 4g、pids_limit: 512。限制 CPU、内存和进程数,约束失控的智能体循环。
- 仅回环端口 — 127.0.0.1:8080:8080。Web UI 只能从宿主机访问,无法从网络访问。
- init 进程 — init: true 添加真正的 PID 1(tini),正确处理信号并回收僵尸进程。
- 重启策略 — restart: unless-stopped。容器崩溃和重启后仍能存活。适用于 docker compose up 启动的服务(如 litellm);run --rm 临时容器不受影响。
- 日志限制 — logging: json-file,max-size: 10m, max-file: 3。防止日志在磁盘上无限增长(每个服务上限约 30 MB)。
构建控制(Dockerfile)
- 多阶段构建 — python3、make、g++ 在 builder 阶段安装,用于编译原生插件 node-addon-require-builtin,但不会复制到运行时镜像。运行时只带 Node + dsh + pnpm。
- 用 pnpm 管理插件 — 在 builder 中安装,并符号链接到运行时。它的存储(PNPM_HOME=/data/.pnpm)位于可写的命名卷上,不在只读 rootfs 上。
- 默认阻止 postinstall 脚本 — npm_config_ignore_scripts=true 加上 pnpm 11 的构建审批门,阻止 npm 生命周期脚本在安装时执行。这消除了恶意插件的主要供应链攻击途径(恶意的 postinstall 无法运行)。受信任插件的覆盖方式见 Plugins。
- 显式 node 入口点 — ENTRYPOINT ["node", "--expose-internals", ".../bin.js"]。dsh 的 HMR 插件需要 --expose-internals;NODE_OPTIONS 不接受这个参数,所以必须作为命令行参数传入。
- 用 socat 为 web 做 TCP 代理 — dsh 拒绝 --host 0.0.0.0(出于 RCE 安全考虑),在容器内只绑定 127.0.0.1,Docker 端口转发无法访问。web-entrypoint.sh 让 dsh 跑在 127.0.0.1:8090,再由 socat 把 0.0.0.0:8080 桥接到 127.0.0.1:8090,这样发布的端口才能用。socat 以非 root 的 node 用户运行;宿主端口仍然只监听 127.0.0.1。
- 一次性 token 认证(dsh 0.1.2+) — dsh 的 web 访问由启动 URL 里的一次性 token 把关。
直接访问 http://127.0.0.1:8080 会返回 401;带 token 的 URL(启动时打印)在首次打开时设置认证 cookie。web-entrypoint.sh 把 dsh 启动 URL 行里的内部端口改写成发布的代理端口,这样用户拿到的是正确的带 token URL。
- 去掉 setuid/setgid — Debian 基础镜像里的二进制文件(su、passwd、mount 等)已移除 setuid/setgid 位。不再留有提权面(CIS Docker Benchmark)。
- 预装开发工具 — 加入 git、curl、jq、less、ripgrep、 openssh-client,方便在容器内开发(搜索、API 测试、分页、git 操作)。这些都是用户态工具,不会开放端口,也不会授予权限。
守护进程隔离(macOS)
在 macOS 上,Docker Desktop 把 dockerd 跑在沙箱化的 Linux 虚拟机里(Apple Virtualization.framework),而不是以 root 身份跑在宿主机上。宿主机没有 docker 组。rootlesskit / dockerd-rootless.sh 只适用于 Linux,这里用不上。
想多加一道逃逸屏障,可以开启 Docker Desktop 的 Enhanced Container Isolation(Settings -> Features),让容器跑在用户态内核(gVisor)下,即使出现内核 CVE 也能挡住逃逸。
权限模型与失效安全默认值
dsh 在容器的操作系统级隔离之上,还强制自己的权限层。它提供两个独立的开关,
打包成命名预设,用户用 /permission 命令切换:
- 沙箱模式 — read-only | workspace-write | danger-full-access (bash 执行器允许写入逃逸到多远)。
- 审批策略 — ask | never(每个动作是否需要人工确认)。
随附的预设表有两项:
- workspace-write — 沙箱 workspace-write + 审批 ask。这是默认值:写入限制在工作区,每个动作都要审批。
- danger-full-access — 沙箱 danger-full-access + 审批 never。
完全访问,无需确认。这是最宽松的一端。
失效安全姿态
设计上失效即关闭 —— 未知或未设置的权限会解析为限制性取值,绝不会解析为允许(Warp 执行配置档背后的同一原则):
- 不注入任何放宽限制的环境变量。 容器只设置 DEEPSEEK_API_KEY、git 身份变量、GIT_CONFIG_SYSTEM, DSH_ISOLATION_PLATFORM 和 IS_SANDBOXED。这两个都不会放宽沙箱,也不会抑制审批,所以 dsh 的严格默认值(workspace-write + ask)对每个新会话都生效。
- 切换到 danger-full-access 是有意为之的会话内操作,通过 /permission 执行,并记录为持久的用户意图——绝不是构建时或环境变量的默认值。
- 凭据清洗是失败即关闭的。 dsh-subprocess 在启动命令前,会剥离任何名称匹配 /KEY|PASSWORD|SECRET|TOKEN/i 的环境变量。匹配即移除,只保留通过校验的幸存者,所以拼错的密钥名会被丢弃,而不是泄露。
纵深防御
dsh 的 workspace-write 沙箱是叠加在容器的操作系统级隔离之上的(只读 rootfs、cap_drop: ALL、no-new-privileges、uid 1000)。即使会话运行 danger-full-access 也无法逃出容器:影响范围始终限于 /workspace 和 /data。
面向未来变更的规则
任何新的自主性或审批开关,都必须默认落在严格一端(ask 或 workspace-write),除非有意选择加入。绝不要以构建或环境默认值的形式注入放宽审批或扩大沙箱的设置。
前置条件
固定工具链
| 组件 | 版本 / 来源 |
| @deepseek-ai/dsh | 0.2.0-rc.2(global-tools/package-lock.json) |
| Node 基础镜像 | 见 Dockerfile(node:…-slim) |
| pnpm(镜像内) | 固定在 global-tools/package.json |
上游 Harness 文档:https://deepseek-harness.github.io/deepseek-harness/
DeepSeek Harness 处于开发者预览阶段。优先使用上面的固定版本;不要在没有重新构建并运行 ./test-hardening.sh 的情况下升级到 npm 的 latest/alpha。
- Docker Desktop 正在运行。
- 本地 oMLX OpenAI 兼容服务器运行在 http://127.0.0.1:8000/v1(宿主机)。
部署
1. 配置 API key
cd ~/dsh-docker
cp .env.example .env # edit .env, set DEEPSEEK_API_KEY and git identity
API key 通过 compose 的 environment 传给容器;它绝不会被烘焙进镜像。.env 已被 gitignore。如果你想让智能体用你的身份提交,还要设置 GIT_AUTHOR_NAME / GIT_AUTHOR_EMAIL(以及对应的 COMMITTER 变量)——宿主机的 ~/.gitconfig 不会被挂载。
2. 创建工作区绑定挂载
mkdir -p workspace
这是智能体唯一能读写的宿主机目录。启动任务前,把要处理的文件放在这里。
3. 构建镜像
docker compose build
产出 dsh-hardened:latest。任何 Dockerfile 或补丁变更后都要重新构建。构建输出里的 gcloud docker-auth 警告是 GCR 仓库凭据助手产生的无害噪音,不影响构建。
4. 播种 oMLX 补丁(一次,以及每次 down -v 之后)
智能体指令由 agents/public/.md(git 跟踪)加上可选的 agents/private/.md(gitignore)构建。见 agents/README.md。私有包只在目录存在时,于播种期间绑定挂载。
sh
./run-dsh.sh seed
把容器变体用的 cordis.patch.yml 复制到 dsh-home 卷内的 /data/profiles/{web,headless}/。这两个文件把 dsh 的 deepseek-official 适配器重新指向 http://host.docker.internal:8000/v1(宿主机上的 oMLX 服务)。
5. 验证部署 sh
dsh boots inside the container:
docker run --rm dsh-hardened:latest --help | head -3
Hardening is active (non-root, no caps, read-only rootfs):
docker compose run --rm --entrypoint sh dsh-headless -c \
'id; grep CapEff /proc/1/status; (echo x > /_t) 2>&1 || echo "rootfs read-only OK"'
The container can reach the host oMLX server:
echo 'fetch("http://host.docker.internal:8000/v1/models").then(r=>console.log("HTTP",r.status)).catch(e=>console.log("ERR",e.message))' \
| docker run --rm -i --entrypoint node dsh-hardened:latest -
预期结果:id 显示 uid=1000,CapEff 全为零,向 rootfs 写入失败,oMLX 的 fetch 返回 HTTP 401(能连通;在设置密钥之前需要鉴权)。要跑任务,见「启动与停止」一节。
启动与停止
dsh 没有后台守护进程。每次调用都是一个临时容器(--rm),生命周期取决于所用的配置档:
Web 配置档(长时间运行,可交互) sh ./run-dsh.sh web # serves the UI at http://127.0.0.1:8080 ./run-dsh.sh web ~/Development/myproject # same, with myproject mounted at /workspace
在前台运行。dsh 0.1.2+ 启动时会打印一个带 token 的 URL(http://127.0.0.1:8080/?token=...)——在浏览器里打开这个完整 URL;不带 token 的裸 URL 会返回 401。token 会设置一个有效期 30 天的鉴权 cookie。包装脚本会自动把这个 URL 复制到 macOS 剪贴板(pbcopy),你可以直接粘贴到浏览器,不用在启动日志里翻找。按 Ctrl+C 停止容器;它会退出并自动删除。
Headless 配置档(一次性) sh ./run-dsh.sh headless "run the tests" ./run-dsh.sh headless "run the tests" ~/Development/myproject # with project mounted
跑一个任务,打印结果,然后自行退出。无需手动停止;容器完成后会自行删除。启动之前,包装脚本会做一次快速失败预检:如果 .env 里没有设置 DEEPSEEK_API_KEY,就直接中止(否则每个请求都必然失败);如果连不上本地 oMLX 服务,只发出警告,仍然继续(网络抖动可能只是暂时的)。
后台 headless 任务
用 --bg 把任务丢到后台,稍后再查看,不用在前台盯着。包装脚本以分离模式启动容器,立即返回,并在后台把任务的完整输出保存到 runs/: sh ./run-dsh.sh headless --bg "run the tests" # returns immediately ./run-dsh.sh headless --bg "run the tests" ~/Development/myproject ./run-dsh.sh headless --bg "run the tests" android # project alias works too 从一项任务到下一项任务的完整生命周期:
1. 启动 — --bg 以分离模式启动容器,打印容器 id 和日志路径。与同步路径不同,容器不会自动删除,要等输出完整写入 runs/ 之后才移除,所以即使你等任务结束后再回来查看,也不会丢任何东西。
2. 实时跟踪(可选)— ./run-dsh.sh logs latest 用 docker logs -f 挂到正在运行的任务上。按 Ctrl+C 只会让你脱离,不会停掉任务。任务结束后,logs 改为打印 runs/ 里已完成的日志。
3. 接收通知 — 任务结束时弹出 macOS 桌面通知(标题 dsh,正文 finished (exit ): <task-slug>),你不用轮询也能知道任务完成了。需要 osascript(macOS 默认自带);没有就什么都不做。
4. 查看状态 — ./run-dsh.sh runs 按从新到旧列出运行日志;仍在运行的任务标为 [running]。./run-dsh.sh runs latest(或 runs <substr>)打印已完成的日志。
5. 清理 — ./run-dsh.sh runs clean [N] 保留最新的 N 份日志(默认 10),删掉其余;正在运行的任务日志永远不会被清理。
通知里的容器退出码(以及追加到每份日志末尾的运行摘要块里的退出码),是在日志流结束后、容器删除之前,通过 docker inspect 读取的。
取消后台任务 sh ./run-dsh.sh stop # stop the most recent background job ./run-dsh.sh stop <substr> # stop a job whose log name contains <substr>
强制杀掉容器(docker rm -f),并删除它的 .cid 附属文件,这样 runs 和 logs 不再把它当作运行中的任务。如果任务已经结束,则改为清理残留的附属文件。runs/ 里的日志文件会保留 —— stop 只杀容器,不删除已捕获的输出。
查看正在运行的内容 sh ./run-dsh.sh status # containers, LiteLLM/oMLX reachability, API key, dsh-home volume usage
docker compose ps # list active containers only
首次运行就绪检查 sh ./run-dsh.sh doctor
一条命令,检查无头运行或 web 运行前需要的所有条件:镜像已构建、.env 存在且设置了 DEEPSEEK_API_KEY、git 身份已配置、workspace/ 目录存在、oMLX 补丁已写入 dsh-home 卷、litellm-config.yaml 存在、LiteLLM 可达、oMLX 可达且至少有一个模型。每一项打印 OK、FAIL 或 WARN,并附修复提示。只要还有 FAIL,就以非零状态退出,因此可以用作预检门禁。
在容器内调试 sh ./run-dsh.sh exec # interactive sh shell inside a fresh hardened container ./run-dsh.sh exec ls /data # run a one-off command ./run-dsh.sh exec sh -c 'id; pwd' # run a shell snippet
在一个全新的加固容器里运行任意命令,隔离条件与无头任务相同(非 root、零 capabilities、只读 rootfs、 /workspace 和 /data 可写)。容器默认的入口点(dsh)被覆盖,所以你拿到的是裸 shell——适合查看 /data、检查已安装的插件,或调试补丁,不用重新构建。使用 --no-deps,因此它绝不会启动 LiteLLM。容器退出后即被删除。
清理(容器、卷、镜像) sh
docker compose down # remove containers and network
Add -v to also delete the dsh-home volume (wipes profiles/sessions/settings):
docker compose down -v
docker image rm dsh-hardened:latest # remove the built image
执行 down -v 之后,重新运行 ./run-dsh.sh seed,把 oMLX 补丁恢复到重建的卷里。
智能体只能读写 /workspace(默认绑定挂载到 ~/dsh-docker/workspace)和 /data(它私有的状态卷)。想让它碰的文件,先放进工作区,或者传入项目路径(见下文)。
会话历史
列出过去的 dsh 会话(id、轮次数、最后活动时间戳、标题): sh ./run-dsh.sh sessions
会话保存在 dsh-home 卷(/data)上,所以 docker compose down 之后仍然存在,只有 down -v 才会清掉。列表通过只读绑定挂载 list-sessions.js,读取容器内的会话投影缓存——不需要重建镜像。
要重新打开会话,启动 Web UI(./run-dsh.sh web),在侧边栏的会话列表里选它。dsh 没有为 web 或 headless 配置档提供命令行恢复标志(它的 --resume 是 tui 配置档的标志,而这里没有附带 tui 配置档),所以恢复是 Web UI 的操作,不是命令行的操作。
运行日志
每次 headless 运行(前台或 --bg)都会用 tee 写入 runs/ 下带时间戳的日志,这样临时容器退出时输出不会丢失。包装脚本会传递 dsh 的真实退出码(POSIX sh 没有 pipefail,所以退出码通过 sidecar 文件捕获,而不是管道),并在日志末尾追加运行摘要:
---- run summary ----
task: run the tests
started: 2026-09-07 14:18:23
duration: 45s
exit code: 0
log: runs/20260907_141823-run_the_tests.log
摘要刻意只限于包装脚本能从外部直接观察到的东西(任务文本、实际耗时、退出码)。轮次数、token 用量和工具调用次数都不包含:它们只存在于 dsh 自己的会话日志里,那份日志把每个事件存成单独的 zstd 压缩帧(每个会话数百帧),格式绑定于一份不断演进的内部 RFC,不是稳定的公开 API——解析它很脆弱,dsh 一升级多半就会坏。
sh
./run-dsh.sh runs # list run logs (newest first); [running] tags live jobs
./run-dsh.sh runs latest # print the most recent run log
./run-dsh.sh runs <substr> # print a log whose name contains <substr>
./run-dsh.sh runs clean [N] # prune all but the newest N logs (default 10); live jobs skipped
runs clean 清理旧日志,保留最新的 N 份(默认 10);某份日志后台任务仍在运行时,永远不会被清理。runs/ 已加入 gitignore。Web(交互式)运行不记录日志——只有无头(无人值守)运行才记录,而丢失输出正是这类运行才会遇到的问题。
任务模板
可复用的无头任务提示词以 Markdown 文件形式放在 templates/ 里。每个文件都是一段自包含的提示词,wrapper 读取后作为任务文本传给 dsh——常用任务不用反复重打,提示词还能纳入版本管理、可复现。
sh
./run-dsh.sh task # list available templates
./run-dsh.sh task review api # run the 'review' template against the api repo
./run-dsh.sh task --bg secaudit api # same, detached; check with logs/runs
<project> 接受与 headless 相同的路径或别名(见 项目别名)。加上 --bg 后,任务以分离模式运行,走完整的后台生命周期(通知、logs、runs、stop)。
内置模板
- test —— 跑测试套件,按 file:line 报告失败,提出并应用最小修复,再跑一遍确认。
- lint —— 跑 linter,能自动修的就自动修,剩下的列出来,附上规则和建议修法。
- review —— 审查未提交的改动,看安全、错误处理、资源泄漏、竞态条件、代码风格。结尾给出 APPROVE / REQUEST CHANGES / BLOCK。
- explore —— 梳理架构(入口点、请求流程、状态存储、测试搭建、依赖),写到 EXPLORE.md。
- deadcode —— 找出未使用的导出、无人引用的文件、不可达分支、孤立依赖。先列清单,再谈删除。
- typings —— 能推断的地方补上类型标注;有歧义的用注释标出来。运行时行为不变。
- changelog —— 读上一个 tag 之后的提交,按 Added / Changed / Fixed / Removed / Deprecated 分组起草 changelog,写到 CHANGELOG-draft.md。
- apidocs —— 扫描路由处理函数,把方法、路径、鉴权、参数、响应、错误写进 API-docs.md。
- triage —— 找到最近一次失败的测试输出,追根因,给出带确切代码的最小修复方案。只提方案——不动代码。
- secaudit —— 审计硬编码密钥、SQL/命令注入、路径穿越、不安全的反序列化、缺失鉴权、不安全的加密。按严重程度排序。
添加自定义模板
往 templates/ 里丢一个 .md 文件——第一行应是 # Title(./run-dsh.sh task 会把它当描述显示出来)。其余内容就是 dsh 收到的任务文本。不用重新构建;模板在运行时从宿主机读取,所以增删改都在下一次调用时生效——没有缓存,也没有守护进程需要重载。
在现有项目上工作
要在现有仓库上工作,把它的路径作为额外参数传进去。wrapper 会把该目录 bind-mount 到 /workspace,智能体直接在你的真实仓库上干活——不用 clone,不用复制,不用同步: sh ./run-dsh.sh web ~/Development/my-api ./run-dsh.sh headless "fix the login bug" ~/Development/my-api
项目别名对于 ~/Development 下的仓库,短别名在任何接受 <project> 的地方都能用(web、headless、headless --bg、task),省去写完整路径: sh ./run-dsh.sh web api ./run-dsh.sh headless --bg "run the tests" android
别名来自被 git 忽略的 projects.local.sh(复制 projects.example.sh 即可)。你改好该文件后,别名示例:
- api → 你的 API 仓库
- android / ios / app → 你的移动端或应用仓库
字面路径照样能用——先解析别名,其余内容原样传给 bind mount。
智能体在 /workspace 看到仓库,对你未提交的改动、分支和 git 历史有完整读写权限。编辑会立刻落到宿主机上的真实文件。
Git 身份
宿主机的 ~/.gitconfig 没有挂载,所以要在 .env 里设置 git 身份: sh
GIT_AUTHOR_NAME="Your Name"
GIT_AUTHOR_EMAIL="you@example.com"
GIT_COMMITTER_NAME="Your Name"
GIT_COMMITTER_EMAIL="you@example.com"
这些会传进容器;容器内提交时用你的身份。
bind mount 仓库的 safe.directory
/workspace 下 bind mount 的仓库归宿主机 uid 所有,不是容器的 uid 1000,所以 git 的 safe.directory 检查会以「可疑所有权」为由拒绝它们。解决办法是构建时把一份系统级 gitconfig 烤进镜像: ini
/etc/gitconfig (baked by the Dockerfile)
[safe]
directory = /workspace
docker-compose.yml 设置 GIT_CONFIG_SYSTEM=/etc/gitconfig,git 就会读它。这替换了早先的 GIT_CONFIG_COUNT=1 / GIT_CONFIG_KEY_0 / GIT_CONFIG_VALUE_0 环境变量三件套,那套在 dsh 里会坏:dsh 启动命令时会清洗凭据(dsh-subprocess),凡是名字匹配 /KEY|PASSWORD|SECRET|TOKEN/i 的环境变量都会被剥掉。GIT_CONFIG_COUNT 逃过了清洗,但 GIT_CONFIG_KEY_0 被剥掉,git 拿到 count 为 1 却没有 key → error: missing config key GIT_CONFIG_KEY_0。GIT_CONFIG_SYSTEM 能逃过清洗(名字里没有敏感关键词),又在 dsh 的 bootstrap 环境变量白名单里,所以直接调用和智能体发起的 git 调用都能拿到它。
git push 用的 SSH 密钥
~/.ssh 是故意不挂载的。如果仓库用 SSH remote,而你需要从容器里 git push,那就生成一把一次性密钥,或者改用带 token 的 HTTPS remote。挂载宿主机 SSH 密钥会把它们暴露给智能体。
安全取舍
传项目路径会把影响范围扩大到那一个目录——这是有意的,因为是你选择暴露它。其他加固措施依然生效(非 root、零 capabilities、只读 rootfs、no-new-privileges)。项目里的密钥(.env、凭据文件)会对智能体可见;挂载前先想清楚你是否愿意。
插件
dsh 配置档可以用插件包扩展。插件管理只有命令行(web UI 的 Plugins 页面显示已安装插件及其设置,但
无法安装或移除它们)。安装通过包装脚本在加固容器内运行,因此受到的管控与智能体本身相同。
安装、列出和移除 sh ./run-dsh.sh plugin web add <package> # install a plugin into the web profile ./run-dsh.sh plugin web list # list installed plugins ./run-dsh.sh plugin web remove <package> # remove a plugin
<package> 可以是任意 npm 包名;dsh 会在配置档目录(/data/profiles/<name>)里转发给 pnpm。声明了 dsh.bundle 的包会作为配置档层加载;普通依赖只会安装,不会分层加载,直到后续版本加入 bundle。
持久化
插件安装结果保存在 dsh-home 命名卷(/data)上:
- docker compose down —— 保留已安装的插件(卷保留)。
- docker compose down -v —— 清空插件、会话、设置和 pnpm store。执行 down -v 后重新运行 ./run-dsh.sh seed,恢复 oMLX 补丁。
恶意插件防护
两道独立的闸门阻止恶意包在安装时运行代码:
1. ignore-scripts 环境变量 —— 拦截 npm 生命周期脚本(preinstall、install、postinstall)。
2. pnpm 11 构建审批 —— 除非显式批准,否则忽略构建脚本。
两道闸门默认都开启。恶意插件的 postinstall 无法执行。
运行时,加载的插件以非特权 node 用户身份在容器内运行,零能力,根文件系统只读 —— 与智能体受到的隔离相同。其影响范围仅限于 /workspace 和 /data。
安装需要原生构建的可信插件
有些正规插件自带原生扩展,需要构建步骤。要允许一次性可信安装的构建脚本,需同时覆盖两道闸门: sh
docker compose run --rm -e npm_config_ignore_scripts= --entrypoint sh dsh-headless \
-c 'cd /data/profiles/web && pnpm add <package> --config.dangerouslyAllowAllBuilds=true'
这样写是刻意繁琐的,好让构建审批成为深思熟虑后的主动行为,而不是默认行为。
安装托管在 git 上的插件
有些插件没有发布到 npm(或者 npm 上的名字已被无关的包占用)。用 github:<owner>/<repo> 从 GitHub 安装。托管在 git 上的插件会运行 prepare 构建脚本,所以两道闸门都必须覆盖: sh
docker compose run --rm -e npm_config_ignore_scripts= dsh-headless \
plugin --profile web add "github:<owner>/<repo>"
镜像包含 ca-certificates,所以 git HTTPS 可用。如果旧镜像上仍有 SSL 错误,设置 GIT_SSL_NO_VERIFY=1(在沙箱容器里是安全的)。
已安装的插件
dsh-better-sidebar(文件编辑器、Git 面板、终端、浏览器)
dsh-better-sidebar 插件为 dsh web UI 增加完整的侧边栏工作区:文件浏览器 + CodeMirror 编辑器、Git 面板(diff/stage/commit)、内嵌浏览器、终端,以及后台任务视图。它安装在 dsh-home 卷上的 web 配置档中。
在侧边栏打开聊天文件(interceptOpenPath,默认开启):聊天中的文件链接(工具行路径、产出文件行、提及)会在
sidebar editor instead of the system default app. This feature hooks dsh's
remote.session.openWorkspacePath funnel, which arrived in dsh 0.1.2 — it was inert on 0.1.1-rc.2 (the wrapper bailed when the method was absent, so clicks fell through to the no-op xdg-open shim). The 0.1.2-rc.1 upgrade activates it. Toggle it in Settings → Side card → "Open chat files in the sidebar".
Terminal tab (node-pty native addon)
The terminal tab depends on node-pty, a native addon. node-pty 1.1.0
ships prebuilds for darwin and win32 but not for linux, so the binary must
be compiled from source. The runtime image has no build tools (kept minimal
for hardening — no python3/make/g++), so pty.node is pre-compiled in the Docker builder stage and baked into the runtime image at /opt/prebuilds/node-pty/pty.node. pnpm's allowBuilds: node-pty: false in the profile's pnpm-workspace.yaml skips the compile at install time (it would fail without build tools); the prebuilt binary is injected afterward.
node-pty uses N-API (ABI-stable), so the 81 KB binary loads across Node.js
versions. The loader (lib/utils.js) checks build/Release/pty.node first,
which is where the injection puts it. On Linux the native pty.node handles
forkpty() directly; the spawn-helper binary the plugin's ensureSpawnHelper
looks for is macOS-only and not needed.
Enable the terminal after installing the plugin:
sh
./run-dsh.sh plugin web add dsh-better-sidebar
./run-dsh.sh enable-terminal # injects pty.node; idempotent
Then hard-refresh the browser (Cmd/Ctrl+Shift+R). The terminal tab opens a
login shell (-l), resolved as an emulator would: an explicitly configured
shell → $SHELL → the passwd login shell → /bin/bash.
Customizing terminal aliases
Edit dsh-aliases.sh in this repo to add, remove, or change the aliases and
shell functions available in the terminal tab. The Dockerfile bakes it into
/etc/profile.d/dsh-aliases.sh, which /etc/profile sources for bash login
shells (the terminal spawns bash -l). It is NOT sourced by dsh's bash tool
(non-login bash -c), so the aliases apply only to the interactive terminal.
After editing, rebuild and restart:
sh
docker compose build
docker rm -f $(docker ps -q --filter name=dsh-web) 2>/dev/null
./run-dsh.sh web
Only include aliases safe for the sandbox: no secrets, no destructive ops,
no host-only paths, and no binaries absent from the image.
Security: the terminal runs a PTY in the same sandbox as the agent — same uid 1000, zero capabilities, read-only rootfs, seccomp filter. A PTY is a standard POSIX facility with no privilege escalation. The agent already has full command execution via the bash tool; the terminal adds interactivity with no expansion of the blast radius (still /workspace and /data).
Reinstall after a volume wipe (docker compose down -v):
sh
1. Re-seed the oMLX patches and default model.
./run-dsh.sh seed
2. Set allowBuilds for node-pty (skip native build without error).
docker compose run --rm --entrypoint sh dsh-headless -c '
cat >> /data/profiles/web/pnpm-workspace.yaml << EOF
allowBuilds:
node-pty: false
EOF'
3. Install the plugin.
./run-dsh.sh plugin web add dsh-better-sidebar
4. Inject the prebuilt node-pty binary to activate the terminal tab.
./run-dsh.sh enable-terminal
After installation, hard-refresh the browser (Cmd/Ctrl+Shift+R) to see the
sidebar.
dsh-at-file — removed (incompatible with dsh 0.1.2-rc.1)
dsh-at-file v0.6.3 (the only published version) imports settingsNamespace
from @deepseek-ai/dsh-settings, an export removed in dsh 0.1.2-rc.1. It
fails to load and breaks the entire plugin tree at boot. There is no newer
version. It was removed from the web profile. The @file hover button in
dsh-better-sidebar's file explorer covers the same use case.
aegis (engineering discipline skills)
aegis is an engineering method pack:
baseline-first planning, systematic debugging, verification before completion,
and repair/retirement tracking. It enforces habits that prevent reworks — the agent aligns with the real codebase state before editing and proves completion with fresh evidence. Pure TypeScript skills, no native deps.
Must be installed from GitHub (the npm name aegis is an unrelated old JS
library):
sh
docker compose run --rm -e npm_config_ignore_scripts= dsh-headless \
plugin --profile web add "github:ganyuanran/aegis"
dsh-context (context window insight)
dsh-context adds a context
insight panel: see what the model's context window contains, per-message token
stats, compression/injection events, and composition vs window size. Useful in
long agentic sessions to understand when and why the agent loses track. Pure UI
plugin, no native deps.
sh
./run-dsh.sh plugin web add dsh-context
MCP servers
MCP (Model Context Protocol) servers are pre-installed in the image and
injected via cordis.patch.yml for predictable, reproducible configuration — no UI setup, no npx, no runtime network fetch.
Memory server (knowledge graph)
The @modelcontextprotocol/server-memory package is pre-installed and
loaded as an mcp-client plugin instance in both web and headless profiles.
It provides persistent knowledge-graph memory across sessions:
- Tools exposed: mcp__memory__create_entities,
mcp__memory__create_relations, mcp__memory__add_observations,
mcp__memory__delete_entities, mcp__memory__delete_observations,
mcp__memory__delete_relations, mcp__memory__read_graph,
mcp__memory__search_nodes, mcp__memory__open_nodes.
- Transport: stdio (spawned as a child process inside the container).
- Memory file: /data/memory.jsonl on the dsh-home named volume — persists across docker compose down (retained); wiped by down -v.
- Config source: patches/{web,headless}/cordis.patch.yml (the insert
block adding the mcp-memory entry).
The memory server is spawned automatically when dsh boots; no manual startup
is needed. To add more MCP servers, add another insert block in the patch
file and rebuild.
LiteLLM router (auto model selection)
dsh points at a LiteLLM proxy (litellm compose service, port 4000) instead of
oMLX directly. LiteLLM's complexity_router classifies each request by
complexity and routes to the appropriate oMLX model — like Warp's "auto":
dsh container → LiteLLM (port 4000) → oMLX (port 8000) → models
classifies + routes
Tiers
| Tier | Model | Active params | When |
| SIMPLE | Qwen3-VL-30B-A3B-Instruct-4bit | 3B | Greetings, simple lookups |
| MEDIUM | Qwen3-VL-30B-A3B-Instruct-4bit | 3B | Standard coding tasks |
| COMPLEX | Qwen3.8-27B-MLX-4bit | 27B | Architecture, multi-file |
| REASONING | Qwen3.6-35B-A3B-4bit | 3B | Deep analysis (thinking mode) |
The heuristic classifier is sub-millisecond (no extra model calls). Context-window
escalation is on by default: if a prompt provably won't fit the chosen model,
it auto-escalates to a bigger one. classification_mode: user_turn classifies
only new user asks and carries the decision through tool-result turns (fewer
classifier calls in agentic sessions).
Default model
dsh's agent-default-model is set to smart-router. Individual models are
also advertised for manual per-session selection in the dsh UI.
Fallbacks
If a model fails, LiteLLM falls back: smart-router → Qwen3-VL-30B-A3B,
Qwen3-VL-30B-A3B → Qwen3.8-27B-MLX-4bit, Qwen3.8-27B-MLX-4bit →
Qwen3-VL-30B-A3B, Qwen3.6-35B-A3B → Qwen3.8-27B-MLX-4bit.
Config
- litellm-config.yaml — model list, auto router tiers, fallback chains.
- The litellm service is a long-running container started with
docker compose up -d litellm. The wrapper starts it automatically before
dsh.
- LiteLLM reaches oMLX via host.docker.internal:8000; dsh reaches LiteLLM via
the compose network (litellm:4000).
- LiteLLM has a healthcheck (/health/liveliness) and restart: unless-stopped.
- Each target model sets drop_params: true per-model (not in
litellm_settings) so unsupported params like dsh's thinking (sent when
reasoningEffort is High) are dropped before reaching oMLX. Setting it
globally would strip the tools parameter from router requests.
oMLX settings tuning
The oMLX server config (~/.omlx/settings.json) was tuned for this setup:
chunked_prefill, hot_cache_max_size: 32GB, initial_cache_blocks: 512,
burst_decode_mode: fast, max_concurrent_requests: 3,
max_context_window: 262144. Backup at ~/.omlx/settings.json.bak.*.
oMLX tool parser fix (structured tool calls)
dsh sends the OpenAI tools parameter and expects structured tool_calls in
the response. Without tool_parser_type set in a model's
tokenizer_config.json, oMLX returns text-formatted tool calls
(<function=bash>...) as content with finish_reason: stop — dsh cannot parse or execute them, so tools appear as raw text in the chat.
The fix: add "tool_parser_type": "qwen3_coder" to each model's
tokenizer_config.json (at ~/.omlx/models/<model>/tokenizer_config.json).
This tells oMLX to parse the model's text output into structured
tool_calls with finish_reason: tool_calls.
Models fixed (backup at tokenizer_config.json.bak):
| Model | tool_parser_type | Status |
| Qwen3-VL-30B-A3B-Instruct-4bit | qwen3_coder | works (fix applied) |
| Qwen3.8-27B-MLX-4bit | qwen3_coder | works (was already set) |
| Qwen3.6-35B-A3B-4bit | qwen3_coder | works (fix applied) |
| Qwen3-Next-80B-A3B-Instruct-MLX-4bit | qwen3_coder | set (not yet verified) |
Note: Qwen3-Coder-30B-A3B-Instruct-4bit was removed from the setup because
it uses a different tokenizer (token IDs 151643/151645 vs 248044/248046) and
returns text-formatted tool calls despite the tool_parser_type setting. The
SIMPLE/MEDIUM tiers now use Qwen3-VL-30B-A3B-Instruct-4bit, which returns
structured tool_calls correctly and adds vision capability.
After applying the fix, restart oMLX so it reloads model configs.
sh
Apply the fix to a model:
python3 -c "
import json
p = '$HOME/.omlx/models/<model>/tokenizer_config.json'
d = json.load(open(p))
d['tool_parser_type'] = 'qwen3_coder'
json.dump(d, open(p, 'w'), indent=2, ensure_ascii=False)
print('done')
"
Then restart oMLX (quit the app and relaunch).
oMLX config
The backup of the original bare-metal dsh config is in ~/dsh-omlx-backup/.
The oMLX server itself is configured via ~/.omlx/settings.json (not part of
this repo). See the LiteLLM router section above for the current routing setup.
Security audit
A full root-privilege audit was performed against the built image and running
container. All root-privilege escalation vectors are neutralized:
- Container user: uid=1000(node) (never root).
- Effective capabilities: CapEff: 0000000000000000 (zero).
- Bounding capabilities: CapBnd: 0000000000000000 (cannot acquire caps).
- NoNewPrivs: 1 — setuid binaries cannot escalate.
- Seccomp: 2 (filter mode) active.
- Root filesystem read-only; /tmp is nosuid,nodev,noexec.
- All root-owned paths (/, /etc, /usr, /var, /root, /home, /opt)
unwritable by the container user.
- sudo not installed; setuid/setgid bits stripped from all base binaries.
- --privileged not used; no host namespaces.
No root privileges are exposed. The blast radius is limited to /workspace
and /data.
Security invariants
The testable properties the hardened image guarantees. Each is phrased as an
invariant that must hold after every build. The ./test-hardening.sh suite
asserts all of them automatically (host static checks + in-container checks
under the real runtime security context); run it after any hardening change.
They are also verified by the smoke commands in Deployment §5
and the Security audit above.
Runtime (docker-compose.yml)
1. Runs as uid 1000, never root — id reports uid=1000(node).
2. Zero effective capabilities — CapEff: 0000000000000000.
3. Zero bounding capabilities — CapBnd: 0000000000000000.
4. NoNewPrivs: 1 — setuid binaries cannot escalate.
5. Root filesystem read-only — writes to / and /etc fail.
6. Only three writable paths: /tmp (tmpfs, noexec,nosuid, 64 MB), /data
(named volume), /workspace (bind mount).
7. /tmp is noexec — no binary execution from tmpfs.
8. No host secrets mounted — ~/.ssh, ~/.aws, ~/.config, browser dirs are not exposed.
9. Web port loopback-only — 127.0.0.1:8080, never the network.
10. Resource-bounded — cpus: 4, mem_limit: 4g, pids_limit: 512.
Build (Dockerfile)
11. No setuid/setgid binaries — find / -xdev -perm /6000 returns nothing.
12. Build tools (python3, make, g++) exist only in the builder stage;
the runtime ships none.
13. /etc/gitconfig baked with safe.directory = /workspace so bind-mounted
repos pass git's ownership check.
14. npm_config_ignore_scripts=true in the runtime — npm/pnpm lifecycle scripts blocked by default (rogue-plugin supply-chain gate).
Permission (dsh + container)
15. dsh's default preset is workspace-write (sandboxed + ask) — the restrictive end; danger-full-access is opt-in per session.
16. No container env var relaxes approval or widens the sandbox.
17. dsh-subprocess scrubs env-vars matching /KEY|PASSWORD|SECRET|TOKEN/i
on every command spawn (fail-closed).
Supply chain (lockfiles)
18. The builder installs global-tools and pty-build with npm ci from
committed package-lock.json files — every tarball fetched by sha512 integrity hash, no "latest" resolution at build time.
19. node-pty is compiled from a verified tarball in the builder; only the
81 KB pty.node binary crosses into the read-only runtime.
Validating hardening
Run the hardening validation suite after any Dockerfile, docker-compose.yml,
or lockfile change to confirm every security invariant still holds:
sh
./test-hardening.sh # build, then run all checks
./test-hardening.sh --no-build # test the current image without rebuilding
Phase A checks the host config statically (compose runtime controls, Dockerfile
build controls, lockfile integrity via jq). Phase B builds the image. Phase C
bind-mounts hardening-checks.sh into a container and runs it under the real
runtime security context (cap_drop ALL, no-new-privileges, read-only rootfs,
/tmp noexec tmpfs) to assert the in-container invariants. The suite exits
non-zero on any failure, so it can gate a pre-deploy check. Requires docker,
docker compose, and jq on the host.
Files
- Dockerfile — multi-stage build (dsh + pnpm + socat + dev tools + MCP memory server, build tools excluded).
- global-tools/ — pinned, integrity-verified dependency tree (package.json + package-lock.json) for the builder-stage global tools (dsh, pnpm, MCP memory server); installed via npm ci.
- pty-build/ — pinned, integrity-verified dependency tree (package.json + package-lock.json) for the node-pty native-addon compile; installed via npm ci.
- docker-compose.yml — hardened service definitions (dsh + litellm router).
- litellm-config.yaml — LiteLLM proxy config: model list, auto router tiers, fallbacks.
- web-entrypoint.sh — web entrypoint: dsh on loopback + socat proxy + token-URL port rewrite.
- register-workspace.sh — pre-registers /workspace in the dsh workspace registry on boot.
- patches/{web,headless}/cordis.patch.yml — LiteLLM router + MCP server patches.
- seed-omlx.sh — seeds patches and the pnpm store into the DSH_HOME volume.
- run-dsh.sh — host wrapper (seed / web / headless [--bg] / plugin / enable-terminal / sessions / runs / logs / stop / exec / task / doctor / status; starts litellm, accepts a project path or alias, preflight-checks the stack before headless jobs, tees headless output to runs/ with a run summary, auto-copies the web token URL to the clipboard, and fires a macOS notification when a background job finishes).
- templates/ — reusable headless job prompts (test, lint, review, explore, deadcode, typings, changelog, apidocs, triage, secaudit); invoked by ./run-dsh.sh task <name> [project].
- list-sessions.js — in-container helper (bind-mounted read-only) that lists dsh sessions from the session-projection cache; invoked by ./run-dsh.sh sessions.
- enable-terminal.sh — injects the prebuilt node-pty binary into the dsh-better-sidebar plugin so the terminal tab works.
- test-hardening.sh — host-side hardening validation suite driver (Phase A host static checks, Phase B build, Phase C in-container checks); exits non-zero on any invariant violation.
- hardening-checks.sh — in-container invariant checks (uid, caps, no-new-privs, read-only rootfs, /tmp noexec, no setuid, no build tools, gitconfig, ignore-scripts, node-pty prebuild, env-scrub pattern, default preset); invoked by test-hardening.sh.
- .env.example — API key and git identity template.
Inspect composed dsh config (optional)
After the image is built, you can dump the effective profile config inside the
container (useful after upgrades):
bash
./run-dsh.sh exec dsh --profile headless --dump-config 2>/dev/null | head
or, if supported by your pin:
./run-dsh.sh exec dsh --help
License and attribution
- This repository's original code (Docker/Compose, host scripts, cordis
patches, agent packs, docs, tests): MIT © Sismotur.
- DeepSeek Harness / @deepseek-ai/dsh (pulled at image build): MIT ©
DeepSeek — full notice in NOTICE.md.
- Upstream project:
deepseek-ai/deepseek-harness.
If you publish container images built from this tree, retain NOTICE.md with
the distribution so DeepSeek's MIT copyright notice travels with substantial
portions of their software embedded in the image.预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
sismotur/dsh-docker
为 DeepSeek Harness(dsh)加固的 Docker 打包方案——独立项目,与 DeepSeek 无关
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,125已汉化MCP
71.7
AI 架构文档生成器sopaco/deepwiki-rs
🚀 Litho 已进化为 Terrain —— 给你的 AI 智能体一张会呼吸的代码库地图。
★ 3,157已汉化DSH
70.8
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
AI 编程纪律方法包GanyuanRan/Aegis
让 AI 编程 agent 变得可信:少返工、更安全、说“完成”前先给证据。
★ 1,384已汉化DSH
69.6
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9