把 DSH(DeepSeek Harness)部署到服务器:手机、电脑实时同步,云端开发
2026-08-16

适用场景:你有一台服务器,想把 DeepSeek Harness 的浏览器界面(Web GUI)跑在上面,让家里的电脑、公司的电脑、甚至手机浏览器都能登录使用,用 AI 帮你写代码、跑任务。本文基于 DSH 0.1.0-rc.6 + Ubuntu 22.04/24.04 + nginx + pm2 的实际部署经验整理。

为什么需要服务器 + 反代

DSH 的 Web 界面(dsh --profile web)默认只监听 127.0.0.1:3080,而且出于安全考虑禁止 --host 0.0.0.0(CLI 直接报错:防止把远程代码执行能力暴露到公网)。所以正确姿势是:

浏览器(手机/电脑)──► nginx(80/443)──► dsh web(127.0.0.1:3080)

nginx 做反向代理 + 登录保护 + 移动端适配,DSH 本身只在本机回环地址提供服务。


架构总览

  • dsh web:实际服务,监听 127.0.0.1:3080
  • dsh-webui-auth 插件: 登录认证(未认证的浏览器拿不到任何资源)
  • pm2: 守护进程 + 开机自启
  • nginx: 对外入口,反代 + 移动端缩放 + manifest 直出

第一步:安装 DSH

需要 Node.js ^22.19.0 || >=24.0.0

npm install -g @deepseek-ai/dsh
dsh --version   # 验证安装

第二步:初始化 web profile 并安装认证插件

dsh --profile web 首次运行时自动初始化 profile(目录在 ~/.dsh/profiles/web)。先装登录认证插件(需要 pnpm,Node 自带 corepack):

corepack enable pnpm
npx @deepseek-ai/dsh plugin --profile web add dsh-webui-auth

之后启动 web 服务,首次访问登录页时创建你的账号密码(存在 ~/.dsh/profiles/web/node_modules/dsh-webui-auth/dsh-webui-auth.json,密码是 scrypt 哈希):

dsh --profile web

补充:固定目录选择器为浏览器模式(服务器远程访问必做)

创建 ~/.dsh/patch.yml

# 固定目录选择器为「浏览器浏览」模式。
# 默认的 directory-picker-auto 会根据服务器环境自动选择:
#   - native:系统目录对话框,需要在服务器本机的显示器上操作;
#   - browse:浏览器内的目录列表(可浏览/新建子目录)。
# 服务器通过 nginx 反代远程访问时,native 对话框会弹在服务器自己的屏幕上,
# 远程的手机/电脑根本看不到,必须固定为 browse。
# 注意:patch 无法直接改已有行的插件 name,所以先禁用 auto 行,
# 再插入 browse 的主机端 + 客户端插件对。

- id: directory-picker
  disabled: true

- insert:
    - id: directory-picker-browse
      name: '@deepseek-ai/dsh-host-directory-picker-browse'
    - id: directory-picker-browse-ui
      name: '@deepseek-ai/dsh-client-ui-directory-picker-browse'

启动时用 --patch 挂上(见第四步的启动参数)。如果服务器上确实没有 DISPLAY/chooser 环境,auto 也会选 browse,但显式固定可以保证任何环境下行为一致。

第三步:配置 API Key

~/.dsh/.credentials.yaml 写入:

DEEPSEEK_API_KEY: sk-你的密钥

(或通过环境变量 DEEPSEEK_API_KEY 注入,二选一即可。)

第四步:pm2 守护 + 开机自启

写 pm2 配置 ~/.dsh/pm2.json

{
  "apps": [
    {
      "name": "dsh-web",
      "script": "/usr/local/bin/dsh",
      "args": "--profile web --patch /root/.dsh/patch.yml --trusted-host 你的域名",
      "autorestart": true,
      "restart_delay": 3000
    }
  ]
}

--trusted-host 你的域名 必须加:Web 界面调用 /api 时有浏览器信任围栏,通过域名访问时 Host 不再是 127.0.0.1:3080,不加这个参数 API 会被拒。

启动并设置开机自启:

pm2 start ~/.dsh/pm2.json
pm2 save
pm2 startup 

第五步:nginx 反向代理

新建 /etc/nginx/sites-available/dsh.apple.me(域名换成你的),软链到 sites-enabled 并 reload:

ln -s /etc/nginx/sites-available/dsh.apple.me /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

下面这份是完整最终配置,包含两个必踩的坑的解法(见注释),gzip/缓存为可选优化:

server {
    listen 80;
    server_name dsh.apple.me;

    # ── 可选:gzip 压缩(纯反代站点,上游不压缩,统一由 nginx 压)──
    gzip on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1k;
    gzip_vary on;   # 必须:与下面的强缓存配合,区分压缩/未压缩变体
    gzip_types text/plain text/css application/json application/javascript
               application/x-javascript text/javascript application/xml
               application/xml+rss image/svg+xml application/manifest+json;

    # ── 可选:静态资源缓存(Vite 产物带内容哈希,可放心长缓存)──
    location ~* ^/assets/.*\.(js|css|woff2?|ttf|eot|svg|png|jpe?g|gif|ico|webp|json|map)$ {
        proxy_pass http://127.0.0.1:3080;
        proxy_http_version 1.1;
        proxy_set_header Host 127.0.0.1:3080;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # ── 坑 2 解法:manifest 由 nginx 直接输出(见下文详解)──
    location = /manifest.webmanifest {
        default_type application/manifest+json;
        return 200 '{
  "id": "/",
  "name": "DeepSeek Harness",
  "short_name": "DSH",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "icons": [
    {
      "src": "/favicon.svg",
      "sizes": "any",
      "type": "image/svg+xml",
      "purpose": "any"
    }
  ]
}';
    }

    # ── 主入口:反代 + 坑 1 解法(移动端缩放)──
    location / {
        proxy_pass http://127.0.0.1:3080;
        proxy_http_version 1.1;
        proxy_set_header Host 127.0.0.1:3080;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";   # WebSocket/SSE 长连接
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;

        # ── 坑 1 解法:手机端 viewport 缩放 ──
        proxy_set_header Accept-Encoding "";     # 必须:让 sub_filter 拿到未压缩 body
        sub_filter 'name="viewport" content="width=device-width, initial-scale=1"'
                   'name="viewport" content="width=device-width, initial-scale=0.7"';
        sub_filter_once on;
    }
}

坑 1:手机浏览器内容区域太小

现象:手机上打开 Web 界面, 工作区域宽度太窄,设置页面几乎没法用。

原因:页面 HTML 里的 viewport meta 是 initial-scale=1, 文字太大。

解法:在 nginx 上用 sub_filter 把响应里的 viewport 改成 0.7,让页面在手机屏上缩小显示。两个要点:

  1. 必须先 proxy_set_header Accept-Encoding ""; 把发给上游的压缩请求头置空,否则上游返回 gzip 压缩的 body,sub_filter 的字符串替换会失效;
  2. sub_filter 默认只处理 text/html,HTML 页面正好符合。

改完 nginx -t && systemctl reload nginx,手机硬刷新(清缓存)后就能看到效果。


坑 2:manifest.webmanifest 请求永远是 302,PWA 装不上

现象:浏览器加载 <link rel="manifest"> 时请求 /manifest.webmanifest 一直被重定向到登录页(302),DevTools 里 manifest 解析失败,手机"添加到主屏幕"不可用,display 模式也控制不了。

原因dsh-webui-auth 插件在 HTTP 层拦截了所有未认证请求(包括静态资源)并 302 到登录页。而浏览器拉取 manifest 的请求不会携带登录 Cookie,于是永远 302,拿不到 manifest 内容。

解法:让 nginx 绕过认证,直接输出这份 manifest 文件——内容照抄 DSH dist 里的 manifest.webmanifest,并把 "display": "fullscreen" 改成 "standalone"(standalone 是"普通应用窗口",手机 PWA 更合适;fullscreen 会全屏隐藏浏览器 UI)。

要点:

  1. location = 精确匹配,只接管这一个路径,不影响其他资源;
  2. 内容硬编码在 nginx 里,以后 DSH 升级如果改了 manifest 结构需要手动同步;
  3. 不用改 DSH 源码,也不用给认证插件开白名单,改动全部在 nginx 层,升级 DSH 不会丢。

访问与验证

  • 电脑:浏览器打开 http://你的域名,登录后正常使用;
  • 手机:同样地址登录。验证 PWA:Chrome 菜单 →「添加到主屏幕」;DevTools → Application → Manifest,display 应为 standalone
  • 如果手机或电脑上看到的是旧页面,记得硬刷新清缓存。

常见问题

  1. 想用 HTTPS?

    用 certbot 申请证书,listen 443 ssl,并把 80 重定向到 443(注意 443 的 location 也要带上同样的 sub_filter 和 manifest 直出配置)

  2. 改完 nginx 不生效?

    nginx -t 先查语法,再 systemctl reload nginx,浏览器硬刷新

  3. 远程访问时"选择目录"没反应,或对话框弹在服务器本机屏幕上?

    检查启动参数里是否带了 --patch /root/.dsh/patch.yml 固定 browse 目录选择器;不固定时 auto 可能选中 native(系统对话框弹在服务器屏幕上,远程用户看不到也点不了)


阅读:27   评论: 0 💬
添加新的评论
Copyright © Longbill 2008-2026