广告位联系
返回顶部
分享到

DeepSeek Harness实现关闭终端也不断线

Windows 来源:互联网 作者:佚名 发布时间:2026-09-17 15:58:40 人浏览
摘要

用dsh(DeepSeek Harness)本地 Web GUI 调试 Agent 时,最扫兴的莫过于:终端窗口一关,服务跟着退出,页面从此打不开。这篇文章先用一分钟讲清为什么进程会随终端关闭而死,再给出一个只需配置

用 dsh(DeepSeek Harness)本地 Web GUI 调试 Agent 时,最扫兴的莫过于:终端窗口一关,服务跟着退出,页面从此打不开。这篇文章先用一分钟讲清"为什么进程会随终端关闭而死",再给出一个只需配置一次、日常只敲两个 4 字符命令的完整方案——dshd 后台启动、dsht 彻底停止,从此关终端不慌,重启不丢会话。

一、场景:你大概率踩过的坑

dsh 是一款在本地运行的 AI Harness 工具(npm 包 @deepseek-ai/dsh),它自带一个 Web GUI,默认地址是:

1

http://127.0.0.1:3080

很多人(包括我)第一次都是这样启动的:

1

dsh web

然后打开浏览器开始干活。干到一半想合上电脑或者清理终端窗口,结果终端一关,Web 也打不开了——因为刚才那条命令是"前台"跑在终端里的。

二、原理:为什么会这样?

在类 Unix 系统(macOS/Linux)里,终端窗口关闭时,shell 退出并会给自己的子进程发送一个 SIGHUP(挂断)信号:

  • 前台运行的进程收到 SIGHUP 后默认直接退出;
  • 于是你的 Web 服务跟着终端一起"殉葬"。

要让服务不随终端退出,思路只有一个:让进程脱离终端会话。常见手段:

手段 特点
nohup … & 忽略 SIGHUP,进程在后台跑,最简单
tmux / screen 独立会话,之后还能 attach 回去看
launchd / systemd 做成系统服务,可开机自启(最重)

判断一个进程是否已经"自由",有一个很直观的办法——看它的父进程(PPID):

1

ps -o pid=,ppid=,command= -p <PID>

PPID 是 1(被 init/launchd 收养)说明已脱离终端;PPID 还是某个 shell,说明它仍挂在终端下,关终端会一起死。

三、一次性配置(约 1 分钟)

下面这套配置只做一次:创建一个启动脚本 + 在 ~/.zshrc 里定义两个命令。日常你只需要记 dshd 和 dsht。

3.1 前置:确认 dsh 已全局安装

代码语言:bash

AI代码解释

1

dsh --version || npm i -g @deepseek-ai/dsh

提示:如果之前用 npx 临时跑过 dsh,全局安装后建议清理一下 npx 缓存里的旧副本(~/.npm/_npx 下对应目录),避免"一份程序两处存放"的困惑。数据不受影响——dsh 的用户数据统一放在 ~/.dsh。

3.2 创建启动脚本 ~/start-dsh.sh

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

#!/usr/bin/env bash

# =============================================================

# start-dsh.sh —— 以"脱离终端"方式启动 dsh Web GUI

# 关闭启动终端后 Web 仍可访问 (http://127.0.0.1:3080)

#

# 用法:

#   bash ~/start-dsh.sh           默认模式: nohup 后台运行(推荐)

#   bash ~/start-dsh.sh --tmux    改用 tmux 会话运行

#   DSH_PORT=8080 bash ~/start-dsh.sh   自定义端口(需与 dsh 配置一致)

#

# 依赖: dsh(全局安装); --tmux 模式另需 tmux

# =============================================================

set -u

PORT="${DSH_PORT:-3080}"

LOG_DIR="${HOME}/.dsh/logs"

LOG_FILE="${LOG_DIR}/web.log"

MODE="${1:-nohup}"

if [ "${MODE}" = "--tmux" ]; then

  MODE=tmux

  shift

else

  MODE=nohup

fi

# ---- 定位 dsh 可执行文件(优先 ~/.local/bin 下的全局安装) ----

DSH_BIN="${DSH_BIN:-}"

if [ -z "${DSH_BIN}" ] && [ -x "${HOME}/.local/bin/dsh" ]; then

  DSH_BIN="${HOME}/.local/bin/dsh"

fi

if [ -z "${DSH_BIN}" ] && command -v dsh >/dev/null 2>&1; then

  DSH_BIN="$(command -v dsh)"

fi

if [ -z "${DSH_BIN}" ]; then

  echo "? 未找到 dsh,请先全局安装: npm i -g @deepseek-ai/dsh" >&2

  exit 1

fi

# ---- 已在运行则只提示,避免重复实例 ----

if lsof -nP -iTCP:"${PORT}" -sTCP:LISTEN >/dev/null 2>&1; then

  echo "? dsh 已在运行: http://127.0.0.1:${PORT}"

  [ -f "${LOG_FILE}" ] && echo "  日志: tail -f ${LOG_FILE}"

  exit 0

fi

mkdir -p "${LOG_DIR}"

echo "→ 启动 dsh (${DSH_BIN}) 端口 ${PORT},模式: ${MODE}"

if [ "${MODE}" = "tmux" ]; then

  if ! command -v tmux >/dev/null 2>&1; then

    echo "? 未安装 tmux,可先安装: brew install tmux" >&2

    exit 1

  fi

  tmux new-session -d -s dsh "exec ${DSH_BIN} web $* >'${LOG_FILE}' 2>&1"

  echo "? 已在 tmux 会话 [dsh] 中启动"

  echo "  返回查看: tmux attach -t dsh   (脱离: Ctrl+B 然后 D)"

else

  nohup "${DSH_BIN}" web "$@" >"${LOG_FILE}" 2>&1 &

  echo "? 已在后台启动 (PID $!)"

fi

echo "  打开地址: http://127.0.0.1:${PORT}"

echo "  查看日志: tail -f ${LOG_FILE}"

3.3 赋予执行权限

1

2

chmod +x ~/start-dsh.sh

bash -n ~/start-dsh.sh && echo "语法 OK"

3.4 在 ~/.zshrc 末尾追加两个命令

1

2

3

4

5

6

7

8

9

10

11

12

# dshd: 后台启动/确认 dsh web(关闭终端不中断),由 ~/start-dsh.sh 实现

alias dshd='bash ~/start-dsh.sh'

# dsht: 彻底关闭 dsh web(按 3080 端口找进程)

dsht() {

  local p

  p=$(lsof -tiTCP:3080 -sTCP:LISTEN 2>/dev/null)

  if [ -n "$p" ]; then

    kill $p && echo "? 已停止 dsh (PID: $p)"

  else

    echo "dsh 未在运行"

  fi

}

3.5 让配置立即生效

1

source ~/.zshrc     # 新开的终端不需要,自动加载

四、日常使用

启动(或确认在运行)

1

dshd

第一次会看到:

1

2

3

4

→ 启动 dsh (/Users/xxx/.local/bin/dsh) 端口 3080,模式: nohup

? 已在后台启动 (PID 12345)

  打开地址: http://127.0.0.1:3080

  查看日志: tail -f ~/.dsh/logs/web.log

如果它已经在运行,则只会提示地址并退出——天然防重复启动。

验证"关终端也不断"

启动用的终端窗口直接关掉,然后新开一个终端验证:

1

2

dshd

# 输出: ? dsh 已在运行: http://127.0.0.1:3080

或者不带任何依赖地验证:

1

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:3080   # 200 即存活

彻底停止

1

2

dsht

# 输出: ? 已停止 dsh (PID: 12345)

没在运行时执行 dsht,会友好地提示 dsh 未在运行,而不是报一堆 kill 用法错误。

查看日志

1

tail -f ~/.dsh/logs/web.log

五、脚本设计解读

这段写给想"知其所以然"的同学。

  • 核心只有一行:nohup "$DSH_BIN" web >"$LOG_FILE" 2>&1 &
    • nohup:让进程忽略 SIGHUP,这是"关终端不死"的关键;
    • >log 2>&1:输出重定向到文件,否则后台进程往已关闭的终端写输出会报错;
    • &:放到后台,shell 立即返回。
  • 为什么用端口判断"是否在运行",而不是 pgrep dsh? 一个进程可能对应多个命令名,pkill -f dsh 这种模糊匹配很容易误杀其他 node 进程。而 Web 服务一定占用 3080 端口,用 lsof -tiTCP:3080 -sTCP:LISTEN 精确定位,最安全。
  • 为什么 dsht 用函数而不是 alias? alias 只是文本替换;函数能写逻辑——没找到进程时给出友好提示,而不是执行 kill(无参数会报 usage 错误)。
  • 可执行文件定位为什么先写死 ~/.local/bin/dsh? 全局 npm 安装后 bin 可能在 ~/.local/bin 等非标准路径。脚本先探测已知路径,再退回 PATH 里的 dsh,保证无论环境变量怎么配都能启动。
  • 数据安全:dsh 的数据(会话、设置、凭据)统一存放在 ~/.dsh,与进程存活无关。所以进程怎么重启都不丢对话历史——这一点让"大胆杀进程"变得毫无心理负担。
  • tmux 模式是加分项:某些场景(想随时 attach 回去看交互输出)用 dshd --tmux,之后 tmux attach -t dsh 即可,逻辑完全对称。

六、常见问题

Q1:我现在的 dsh 是前台跑的,怎么平滑切换到后台模式?

先在前台那个终端按 Ctrl+C 停掉旧进程(页面会短暂断开,正常),再执行 dshd,最后刷新 http://127.0.0.1:3080。历史会话不会丢。

Q2:想换端口怎么办?

DSH_PORT=8080 dshd。注意端口需与 dsh 自身配置保持一致;dsht 默认按 3080 找进程,如换端口需要同步修改。

Q3:电脑重启后呢?

进程会随关机结束,开机后执行一次 dshd 即可。想做到开机自启,可以把它包成 launchd(macOS)或 systemd service(Linux),这是另一篇文章的话题。

Q4:关终端后网页打不开,怎么排查?

依次检查:① dshd 是否提示"已在运行";② lsof -nP -iTCP:3080 -sTCP:LISTEN 是否有输出;③ tail -n 50 ~/.dsh/logs/web.log 看启动报错;④ 浏览器是否走了代理导致访问不到 127.0.0.1。

七、小结

场景 命令
后台启动 / 确认在运行 dshd
彻底关闭 dsht
看日志 tail -f ~/.dsh/logs/web.log

三个避坑提醒,写代码和运维时都用得上:

  • 关终端 ≠ 停止服务:只要用 nohup/tmux 启动,进程就归 init 管,终端只是"遥控器";
  • 杀进程按端口、不按名字:lsof -tiTCP:<port> 定位永远比 pkill -f 安全;
  • 数据与进程分离:会话在 ~/.dsh,进程随便重启,历史不丢。

配置一次,之后每次只需 dshd 四个字符——把时间留给真正的问题,而不是和终端窗口搏斗。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计