Files
deploy.stack/shell/dsh_service.md
T

213 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 将 `npx @deepseek-ai/dsh web` 以系统服务方式启动
> 目标:让 DeepSeek Harness Web GUI(默认 `http://127.0.0.1:3080`)随系统自动启动、后台常驻、崩溃可自愈。
> 本文档含完整步骤、脚本与三种方案(Windows 任务计划程序 / NSSM 服务 / Linux systemd)。
## 0. 背景事实(已核对源码)
- `dsh web``dsh --profile web` 的别名;它只启动一个前台 HTTP 服务,**不会自动打开浏览器**,适合作为服务运行。
- 应用参数只有三个:
- `--host <host>`:仅允许 `127.0.0.1``0.0.0.0` 被故意拒绝,防远程代码执行);
- `--port <port>`:默认 `3080`,传 `0` 由系统分配;
- `--trusted-host <authority...>`:浏览器信任白名单,可重复。
- 进程的**当前工作目录(cwd)就是默认 workspace 根**sandbox 的 `workspaceRoot`),服务必须显式设置工作目录。
- 数据根目录默认是 `~/.dsh`(按账户 homedir 计算)。服务账户不同 → `DSH_HOME` 不同 → 凭据/会话/设置全变,务必显式设置 `DSH_HOME`
- 凭据来源:`DEEPSEEK_API_KEY` 环境变量,或 `$DSH_HOME/.credentials.yaml`(Web「设置 → Models」页面写入的那个)。
- 服务进程无终端,权限审批(approval)会挂在 Web 界面里等用户点击,属正常行为。
## 1. 核心原则
1. **不要直接把 `npx` 塞进服务**`npx` 走带哈希的 npm 缓存,服务账户下 PATH/缓存经常对不上。先全局安装,再用 `node.exe` 直接调 CLI 入口。
2. **工作目录必须设**:设为实际 workspace 根(如 `D:\DSH`)。
3. **显式设置 `DSH_HOME`**:指向用户现有 `~/.dsh` 并确保服务账户可读写,或让服务以用户账户运行。
4. **准备凭据**`DEEPSEEK_API_KEY` 环境变量或 `$DSH_HOME/.credentials.yaml`
### 本机路径表
| 项 | 路径 |
|---|---|
| node.exe | `D:\sdk\nodejs\node.exe` |
| 全局 npm 前缀 | `D:\sdk\nodejs` |
| 全局包入口 | `D:\dsh-service\global\node_modules\@deepseek-ai\dsh\lib\bin.js`(离线副本,含 `D:\dsh-service\global\dsh.cmd` shim |
| 默认 DSH_HOME | `C:\Users\cnphp\.dsh` |
| 服务目录 | `D:\dsh-service\`(启动脚本、systemd 参考文件、日志) |
### 前置准备(管理员 PowerShell
```powershell
# 方式一:正常全局安装(本机代理下可能失败,见常见坑 6)
npm install -g @deepseek-ai/dsh
# 方式二:使用离线副本(本机已就绪,与当前 GUI 同版本 0.1.0-rc.6
D:\dsh-service\global\dsh.cmd web --help # 验证可运行
```
## 2. 方案 A:Windows 任务计划程序(最简单,无需额外工具)
适合「开机/登录后自动跑」。`-AtStartup` 需要管理员;`-AtLogOn` 不需要。
### 步骤
1. 保存启动脚本 `D:\dsh-service\start-dsh-web.cmd`(内容见第 5 节,内含环境变量与端口守护)。
2. 在管理员(或普通用户)PowerShell 执行下面的注册命令。
3. `Start-ScheduledTask -TaskName DSHWeb` 立即测试。
### 注册脚本(登录后自动运行,交互式账户,无需密码)
```powershell
$action = New-ScheduledTaskAction -Execute "cmd.exe" `
-Argument '/c "D:\dsh-service\start-dsh-web.cmd"' `
-WorkingDirectory "D:\DSH"
$trigger = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
$settings = New-ScheduledTaskSettingsSet -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) `
-ExecutionTimeLimit 0 -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Limited
Register-ScheduledTask -TaskName "DSHWeb" -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force
Start-ScheduledTask -TaskName "DSHWeb"
```
### 注册脚本(开机即起、不登录也运行,管理员)
需要用户密码(`-LogonType Password`)或以 SYSTEM 运行(`-LogonType ServiceAccount -UserId SYSTEM`,此时 `DSH_HOME` 必须显式指向用户 `.dsh` 且 SYSTEM 有读写权限——本机已具备):
```powershell
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "DSHWeb" -Action $action -Trigger (New-ScheduledTaskTrigger -AtStartup) -Settings $settings -Principal $principal -Force
```
## 3. 方案 BNSSM 真·Windows 服务(推荐:开机即起 + 崩溃自愈)
### 步骤
1. 安装 NSSM(任选其一):
```powershell
winget install NSSM.NSSM --accept-package-agreements --accept-source-agreements # 已在本机执行成功
# 或官网下载 https://nssm.cc/release/nssm-2.24.zip 解压(nssm.cc 在本机网络下直连不稳,优先 winget)
```
2. 管理员 PowerShell 执行下方安装/配置命令(逐条)。
3. `nssm start DSHWeb` 启动服务。
### 安装与配置命令
```powershell
nssm install DSHWeb "D:\sdk\nodejs\node.exe" "D:\dsh-service\global\node_modules\@deepseek-ai\dsh\lib\bin.js web --port 3080"
nssm set DSHWeb AppDirectory "D:\DSH" # workspace 根,必设
nssm set DSHWeb AppEnvironmentExtra DSH_HOME=C:\Users\cnphp\.dsh DEEPSEEK_API_KEY=sk-你的key
nssm set DSHWeb AppStdout "D:\dsh-service\stdout.log" # 日志(含 URL 行)
nssm set DSHWeb AppStderr "D:\dsh-service\stderr.log"
nssm set DSHWeb AppExit Default Restart # 非零退出自动重启
nssm set DSHWeb Start SERVICE_AUTO_START
nssm start DSHWeb
```
> 说明:
> - 默认以 LocalSystem 运行;本机 `C:\Users\cnphp\.dsh` 的 ACL 已含 `NT AUTHORITY\SYSTEM:(OI)(CI)(F)`,无需额外授权。
> - 想以用户账户运行:`nssm set DSHWeb ObjectName ".\cnphp" "你的Windows密码"`(需要密码)。
> - 与方案 A 同开时的端口冲突由 `start-dsh-web.cmd` 里的端口守护解决:谁先占 3080 谁服务,另一个静默退出,不会重启循环。
> - 若 `DEEPSEEK_API_KEY` 未设,DSH 会从 `%DSH_HOME%\.credentials.yaml` 读取,可省略该环境变量。
## 4. 方案 CLinux systemd
参考文件已生成:`D:\dsh-service\dsh-web.service`。
```ini
# /etc/systemd/system/dsh-web.service
[Unit]
Description=DeepSeek Harness Web
After=network.target
[Service]
Type=simple
User=youruser
WorkingDirectory=/path/to/workspace
Environment=DSH_HOME=/home/youruser/.dsh
# Environment=DEEPSEEK_API_KEY=sk-xxx
ExecStart=/usr/bin/node /usr/lib/node_modules/@deepseek-ai/dsh/lib/bin.js web --port 3080
Restart=on-failure
[Install]
WantedBy=multi-user.target
```
```bash
npm i -g @deepseek-ai/dsh
sudo cp dsh-web.service /etc/systemd/system/
sudo systemctl enable --now dsh-web
```
## 5. 启动脚本 `D:\dsh-service\start-dsh-web.cmd`(完整内容)
```cmd
@echo off
rem ============================================================================
rem DeepSeek Harness Web - service launcher
rem Design: both the NSSM service and the scheduled task point at this script;
rem a port guard makes them coexist: whoever reaches port 3080 first serves it,
rem the other exits silently (0 = no restart loop).
rem ============================================================================
set DSH_HOME=C:\Users\cnphp\.dsh
rem Optional: uncomment to force the key (default: loaded from %DSH_HOME%\.credentials.yaml)
rem set DEEPSEEK_API_KEY=sk-xxxx
rem --- Port guard: if 3080 is already listening, exit quietly ---
powershell -NoProfile -ExecutionPolicy Bypass -Command "try { if (Get-NetTCPConnection -LocalPort 3080 -State Listen -ErrorAction Stop) { exit 0 } } catch { }"
cd /d D:\DSH
"D:\sdk\nodejs\node.exe" "D:\dsh-service\global\node_modules\@deepseek-ai\dsh\lib\bin.js" web --port 3080
```
> 若走 NSSM 直接跑 node(不经脚本),则端口守护缺失,需保证同一时刻只有一种方式在运行。
## 6. 验证
```powershell
Get-Service DSHWeb # 或 nssm status DSHWeb
Get-ScheduledTask -TaskName DSHWeb # State = Ready
Invoke-WebRequest http://127.0.0.1:3080 -UseBasicParsing # 返回 200 即 OK
Get-Content D:\dsh-service\stdout.log -Tail 20 # 应看到 "dsh web: http://127.0.0.1:3080"
```
## 7. 维护 / 卸载
```powershell
# 方案 BNSSM
nssm restart DSHWeb # 重启
nssm stop DSHWeb; nssm remove DSHWeb confirm # 卸载
# 方案 A(任务计划程序)
Stop-ScheduledTask -TaskName DSHWeb
Unregister-ScheduledTask -TaskName DSHWeb
```
## 8. 常见坑
1. **端口冲突**:不要和正在运行的 npx 实例同时占用 3080(服务监听失败会以非零状态退出)。先停旧实例(`Get-Process node` 排查),或重启机器后服务自动接管。
2. **工作目录**:不设置时 DSH 把 cwd 当 workspace 根,可能落到 `C:\Windows\System32`,务必设置 `AppDirectory` / `WorkingDirectory`。
3. **账户与 DSH_HOME**:服务账户改变会导致 `~/.dsh` 换位置,凭据、会话、设置全部"消失"。要么显式 `DSH_HOME`,要么服务跑在用户账户下。
4. **局域网访问**`--host 0.0.0.0` 被 DSH 故意拒绝;需要局域网访问时保持 `127.0.0.1` 并用 `--trusted-host` 加白名单。
5. **权限审批**:服务无终端,approval 会挂在 Web 界面里等待点击,不是故障。
6. **npm 全局安装失败(`Exit handler never called!` / ECONNRESET**:本机 npm 使用 `registry.npmmirror.com` 镜像,但环境变量 `HTTPS_PROXY=socks5://127.0.0.1:7890`——**npm 不支持 socks5 协议**,会把镜像连接全部重置导致安装失败。修正:7890 是 mixed 端口(HTTP/SOCKS5 都收),改用 HTTP 协议代理再装:
```powershell
$env:HTTP_PROXY='http://127.0.0.1:7890'; $env:HTTPS_PROXY='http://127.0.0.1:7890'
npm install -g @deepseek-ai/dsh
```
(直连 npmmirror 在本机也不通,必须走代理。)
**兜底方案(本机已采用)**:`npm install -g` 在代理下反复崩溃(`Exit handler never called!` —— 单包 tarball 下载正常,npm 并发拉取时代理必崩)。把已完整可用的 npx 缓存复制到稳定目录即可离线使用:
```powershell
robocopy "$env:LOCALAPPDATA\npm-cache\_npx\<hash>\node_modules" 'D:\dsh-service\global\node_modules' /E
# 验证:
"D:\sdk\nodejs\node.exe" 'D:\dsh-service\global\node_modules\@deepseek-ai\dsh\lib\bin.js' web --help
```
## 附录:本机当前执行状态(2026-xx-xx 会话)
- ✅ `D:\dsh-service\start-dsh-web.cmd` 已生成
- ✅ `D:\dsh-service\dsh-web.service`Linux 参考)已生成
- ✅ 方案 A:任务计划程序 `DSHWeb` 已注册(AtLogOn 交互式,State=Ready
- ✅ 方案 BNSSM 已通过 `winget install NSSM.NSSM` 安装(命令别名 `nssm` 已添加)
- ✅ 全局 CLI 已就绪:`D:\dsh-service\global\node_modules\@deepseek-ai\dsh\lib\bin.js`(离线复制自 npx 缓存,版本 0.1.0-rc.6,已验证 `web --help` 正常;`npm install -g` 两次均因代理问题失败,见常见坑 6)
- ⏸ 端口 3080 当前被会话中的 npx 实例占用,服务/任务注册后未启动;下次开机(无 npx 实例)由 NSSM 或任务自动接管
- 备注:nssm.cc 在本机网络(socks5 代理 127.0.0.1:7890)下直连下载被重置,请用 winget 方式安装 NSSM