• 从系统服务到桌面应用:DeepSeek Harness 部署与多形态使用指南


    背景

    DeepSeek Harness(简称 DSH)是 DeepSeek 推出的 AI 编程助手工具,通过 dsh web 可以在浏览器中访问其 Web UI 界面。但源码编译版本没有现成的桌面客户端,每次都需要手动敲命令启动,终端关闭后服务也就断了。

    本文将完整记录如何通过 WinSW 将源码编译的 DSH 注册为 Windows 系统服务实现开机自启,并进一步利用 Edge 浏览器的“新建应用”功能,将 Web UI 封装为独立的桌面应用。最终效果是:开机后 DSH 自动在后台运行,双击桌面图标即可使用,体验接近原生客户端。

    在部署过程中,还会遇到一个典型问题:服务化后 Web UI 中无法选择工作区文件夹。本文也会给出完整的原因分析和解决方案。

    一、WinSW 服务化部署

    1.1 为什么选择 WinSW

    WinSW 是一个轻量级的开源工具,可以将任意可执行文件包装成 Windows 原生服务,使其在系统后台自动运行,无需用户登录或手动干预。与使用 pm2、nssm 等方案相比,WinSW 的优势在于配置简单(单个 XML 文件)、原生服务集成、支持开机自启和崩溃自动重启。

    1.2 核心配置要点

    首先在 WinSW 的 GitHub Releases 页面下载最新版本的可执行文件,将其重命名为 deepseek-harness.exe,并在同目录下创建同名 XML 配置文件 deepseek-harness.xml。

    配置文件的核心内容如下(请将示例路径替换为你自己的实际路径):

    <service>
      <id>deepseek-harnessid>
      <name>DeepSeek Harness Servicename>
      <description>DeepSeek Harness Web UI Servicedescription>
    
      
      <executable>C:\path\to\node.exeexecutable>
    
      
      <arguments>"D:\path\to\deepseek-harness\apps\cli\lib\bin.js" --profile web --no-openarguments>
    
      
      <workingdirectory>D:\path\to\deepseek-harnessworkingdirectory>
    
      <startmode>Automaticstartmode>
    
      <logmode>rotatelogmode>
      <onfailure action="restart" delay="10 sec"/>
    service>
    

    几个容易踩坑的地方:

    • 必须指向 node.exe 的绝对路径,不能直接写 node,因为服务模式下 PATH 环境变量可能不完整。通过 nvm 管理的 Node.js 路径通常在用户目录下,需要写完整路径。
    • 不要有多余的子命令。DSH 的 CLI 入口 bin.js 本身就是命令入口,不需要再传 dsh 作为子命令,否则会导致参数解析错位。
    • 设为 Automatic 才能实现开机自启。
    • 设为项目根目录,确保程序在相对路径下查找资源时不会出错。

    1.3 安装与管理

    以管理员身份打开 CMD,进入 WinSW 所在目录后执行:

    .\deepseek-harness.exe install
    .\deepseek-harness.exe start
    

    如需卸载,执行 .\deepseek-harness.exe uninstall。启动后访问 http://127.0.0.1:3080 即可看到 Web UI。

    二、理解 DSH 的认证机制

    2.1 为什么直接访问 3080 会被拒绝

    DSH Web UI 内置了浏览器启动令牌认证机制。每次服务启动时,DSH 进程会生成一个随机的启动令牌,只有通过带令牌的 URL 访问(格式为 http://127.0.0.1:3080/?token=...),浏览器才能将令牌交换为一个持久化的 Cookie。

    这是 DSH 的安全设计:Web Host 以当前操作系统用户的权限运行工具型会话,如果仅凭 loopback 地址就授予本地权限,任何能向该端口发请求的进程都可以调用工具。启动令牌认证确保只有真正通过 dsh web 命令启动的浏览器会话才能获得访问权限。

    理解这两者的关系对日常使用非常重要:

    • 启动令牌:每次 dsh web 进程启动时随机生成,进程结束后即消失,绝不持久化。
    • 会话 Cookie:通过令牌交换得到的签名 Cookie,其 HMAC 密钥持久化存储在 $DSH_HOME/.credentials.yaml 中。Cookie 的默认有效期为 30 天,绑定到具体的主机名和端口。

    因此,重启系统后启动令牌会失效,但已签发的 Cookie 不会失效。只要 .credentials.yaml 文件未被删除,且 Cookie 未过期,浏览器再次访问时无需重新认证。

    2.3 作为系统服务时如何首次认证

    由于配置中使用了 --no-open 参数,DSH 不会自动打开浏览器,启动令牌 URL 会输出到 WinSW 的日志文件中。首次使用时,打开 deepseek-harness.out.log,搜索 ?token= 找到完整 URL,复制到浏览器打开即可完成首次认证。之后 Cookie 会持久保存在浏览器中,日常使用无需再关心令牌。

    三、解决工作区文件夹无法选择的问题

    服务化部署后,一个常见的困扰是:在 Web UI 中点击“选择工作区”时,弹窗不出现或直接报错,导致无法选中文件夹。

    3.1 根本原因:Session 0 隔离

    Windows 服务默认运行在 Session 0,这是一个与用户登录会话(Session 1+)完全隔离的非交互式环境。而 DSH 的 directory-picker-auto 后端在检测到 win32 平台且绑定 127.0.0.1 时,会优先选择 native 后端,也就是调用 Windows 原生文件夹对话框(IFileOpenDialog)。

    在 Session 0 中启动的原生对话框,对于你在 Session 1 中操作的浏览器来说是完全不可见的。同时,服务账户(LocalSystem)尝试读取用户主目录时,也可能因权限不足而返回 EPERM 错误,导致应用内目录浏览器无法加载。

    3.2 解决方案:强制使用 browse 后端

    DSH 官方为这种场景提供了 fallback 方案:通过 profile patch 禁用 native 选择器,强制使用 browse 后端(在 Web UI 内部渲染目录浏览器),这样就不依赖 Windows 原生弹窗了。

    第一步:创建 patch 文件

    在你的 DSH 项目目录下创建一个文件,例如 service-picker-browse.yml,内容如下:

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

    这段配置的作用是:禁用默认的自动选择逻辑,同时插入 browse 后端的 Host 与 Client 两侧组件。

    第二步:修改 WinSW 启动参数,注意 --patch 的位置

    在 deepseek-harness.xml 的 中,加上 --patch 参数,指向你刚创建的文件(使用绝对路径):

    <arguments>"D:\path\to\deepseek-harness\apps\cli\lib\bin.js" --profile web --patch "D:\path\to\deepseek-harness\service-picker-browse.yml" --no-openarguments>
    

    这里有一个非常关键的细节:--patch 必须放在 --profile web 之后、--no-open 之前。

    DSH 启动器只识别最前面的全局参数(--profile、--patch),一旦遇到应用自己的参数(如 --no-open),后面的内容全部归应用管理。如果写成:

    node bin.js --profile web --no-open --patch "xxx.yml"
    

    启动器在 --no-open 之后就不再解析全局选项了,--patch 会被当作 web 应用的参数,报 unknown option '--patch'。

    第三步:重新安装并启动服务

    .\deepseek-harness.exe uninstall
    .\deepseek-harness.exe install
    .\deepseek-harness.exe start
    

    之后再次访问 Web UI,点击“选择工作区”时,将会在网页内部弹出目录浏览器,不再依赖 Windows 原生对话框。

    3.3 备选方案:如果版本不支持 --patch

    --patch 是 DSH 启动器级别的全局选项,官方文档中确实存在。但 DSH 处于 developer preview 阶段,CLI 选项在不同 rc 版本间可能发生变化。你可以先用 node bin.js --help 确认当前版本是否支持。

    如果确实不支持,最稳妥的做法是把 patch 内容直接写进 profile 的持久化配置文件,这样每次启动都会自动生效,不需要命令行参数。

    找到 profile 的 patch 文件

    DSH 的 profile 位于 $DSH_HOME/profiles//,其中包含用户自己的 cordis.patch.yml。Windows 上 DSH_HOME 通常为 C:\Users\<用户名>\.dsh,所以 web profile 的 patch 文件路径是:

    C:\Users\<用户名>\.dsh\profiles\web\cordis.patch.yml
    

    将 patch 内容写入该文件

    把之前准备的 service-picker-browse.yml 内容直接追加到 cordis.patch.yml 中:

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

    恢复 WinSW 配置中的 arguments

    去掉 --patch 参数,恢复为:

    <arguments>"D:\path\to\deepseek-harness\apps\cli\lib\bin.js" --profile web --no-openarguments>
    

    重新 uninstall → install → start 即可。

    如果你希望这个修改对所有 profile 生效,可以写入 $DSH_HOME/cordis.patch.yml(即 C:\Users\<用户名>\.dsh\cordis.patch.yml)。home 级 patch 的优先级高于 profile 级 patch,且被所有 profile 共享。

    3.4 其他可能干扰因素

    如果替换为 browse 后端后仍然无法选择,可以检查以下几点:

    1. 路径中包含中文字符:DSH 的原生选择器在读取 UTF-16 路径时存在一个已知 bug,遇到包含 U+XX00 这类码点的汉字(如“开” U+5F00)会错误截断路径,导致工作区创建失败。browse 后端不受此影响,但如果之前用 native 后端选过中文路径,可能残留了无效的工作区记录,需要清理后重试。
    2. 浏览器扩展干扰:部分扩展(如 Page Assist)会改写发往 127.0.0.1 的请求头,导致 DSH 的 Origin 校验返回 HTTP 403,表现为“选择工作区”报错 transport failure for /api/host.pickDirectory。可以尝试用 http://localhost:3080 替代 http://127.0.0.1:3080 访问,或在扩展管理中临时禁用相关扩展。
    3. 旧实例占用端口:如果之前以其他方式启动过 DSH,端口 3080 可能被旧进程占用,导致服务启动异常。可以用 netstat -ano | findstr 3080 检查并结束残留进程。

    四、多种使用方式

    4.1 方式一:浏览器直接访问

    最基础的方式:打开浏览器,输入 http://127.0.0.1:3080,完成首次令牌认证后即可使用。

    4.2 方式二:Edge 将 Web UI 安装为桌面应用

    这是体验提升最明显的一步。Microsoft Edge 内置了将网站安装为独立应用的功能,可以将 DSH Web UI 变成一个拥有独立窗口、独立任务栏图标的桌面应用,就像本地软件一样从桌面直接启动。

    操作步骤:

    1. 在 Edge 中打开 http://127.0.0.1:3080 并完成首次令牌认证;
    2. 点击浏览器右上角的三个点菜单按钮;
    3. 依次选择“应用” → “将此站点安装为应用”;
    4. 在弹出的对话框中输入应用名称(如“DeepSeek Harness”),确认安装;
    5. 安装完成后,桌面上会生成应用图标,也可以右键选择“固定到任务栏”。

    安装后的应用窗口没有浏览器地址栏和标签栏,只展示 DSH 的 Web UI 界面,视觉上就是一个独立的桌面程序。由于 Cookie 是持久化的,通过应用图标打开的窗口会直接进入 UI,无需重新认证。

    如果需要使用多个 DSH 实例(例如不同的 profile 或端口),可以利用 Edge 的多配置文件功能。为每个实例创建一个独立的 Edge 配置文件,然后分别将其安装为独立应用,每个应用都有自己独立的 Cookie 存储,互不干扰。

    4.3 方式三:批量管理脚本

    如果需要在多台机器上部署,可以编写批处理脚本简化服务安装流程。创建一个 install.bat:

    @echo off
    cd /d %~dp0
    deepseek-harness.exe install
    deepseek-harness.exe start
    echo 服务安装并启动完成。
    pause
    

    以及对应的 uninstall.bat:

    @echo off
    cd /d %~dp0
    deepseek-harness.exe stop
    deepseek-harness.exe uninstall
    echo 服务已卸载。
    pause
    

    将 WinSW 可执行文件、XML 配置和这两个脚本放在同一目录,双击即可完成部署。

    4.4 方式四:结合第三方桌面壳(进阶)

    社区已经有一些项目将 DSH Web UI 封装为桌面程序,例如 dsHell 通过薄壳设计复用已安装的 Node.js 和 DSH,双击 exe 即可启动,自动完成令牌交换流程。还有 dsh-window 插件,提供基于 WebView2 的 Windows 原生窗口,安装插件后 DSH 会自动确保桌面应用就绪。这些方案适合希望进一步简化操作的用户,但需要额外安装第三方组件。

    五、总结

    使用方式 启动方式 适用场景
    浏览器直接访问 手动输入 URL 临时使用、调试
    Edge 安装为应用 双击桌面图标 日常使用,体验最佳
    多 Profile 独立应用 多个图标独立启动 多实例并行使用
    第三方桌面壳 双击 exe 追求开箱即用

    整体方案的核心链路是:源码编译 → WinSW 服务化 → 开机自启 → 首次令牌认证 → 解决工作区选择问题 → Edge 应用化 → 日常双击使用。

    这套方案充分利用了 Windows 原生的服务管理和 Edge 浏览器的 PWA 能力,在不引入额外框架的前提下,将 DSH 从一个需要手动管理的命令行工具,变成了开机即用、体验接近原生客户端的桌面应用。

    部署过程中最容易踩的两个坑,一是 WinSW 启动参数顺序(--patch 必须在 --no-open 之前),二是 Session 0 隔离导致原生文件夹选择器不可用(需切换到 browse 后端)。理解这两点后,整个部署过程会顺畅很多。

    需要注意的是,当前 DSH 仍处于 developer preview 阶段,后续版本可能带来破坏性变更,升级时建议关注官方 release notes。

  • 相关阅读:
    Scratch软件编程等级考试一级——20220320
    leetcode每天5题-Day33
    消息中间件篇之Kafka-消费顺序性
    复制粘贴(二):操作剪贴板 navigator.clipboard
    到底什么是上采样、下采样
    【BOOST C++ 5 】通信(04 协程 )
    ElasticSearch复合查寻
    基于springboot的校园跑腿系统
    详解js中的console对象
    Java项目的程序里为什么老用注解?注解有哪些作用
  • 原文地址:https://www.cnblogs.com/znlgis/p/22954862