浏览器里的真实终端DSH-better-sidebar用xterm.jsnode-pty实现真Shell的完整原理含断线重连回放【免费下载链接】DSH-better-sidebar开放的侧边栏底座支持三方拓展注册新侧边栏页面。内置文件渲染编辑/终端/侧边对话/Git/子代理页面 Open sidebar foundation, supports third-party extensions to register new sidebar pages. Built-in file rendering/editing, terminal, side chat, Git, and sub-agent pages.项目地址: https://gitcode.com/gh_mirrors/ds/DSH-better-sidebarDSH-better-sidebar 是一款 DSH 网页插件它在浏览器侧边栏里内置了文件编辑、终端、侧边对话、Git 与子代理页面。其中的终端不是模拟出来的假终端而是基于xterm.js node-pty WebSocket实现的真实 Shell页面刷新、切换会话、网络抖动之后还能重连回同一个进程并把断线期间的历史输出完整回放。下面用最少代码量完整讲清这条链路的原理。为什么浏览器终端分前后两半浏览器出于安全限制不能直接启动系统进程所以浏览器终端必然拆成两半组件位置职责node-pty服务端Node 进程内真正fork出 shell 进程分配伪终端pty持有进程生命周期WebSocket双向通道把键盘输入送到 pty把 pty 输出流推回浏览器xterm.js浏览器前端解析 ANSI 转义序列、渲染字符网格、处理光标与滚动这个项目里前后两半分别落在 src/client/TerminalView.tsx 与 src/pty-manager.ts、src/index.ts。理解终端的一切行为包括断线重连本质上就是理解这两半如何续命。node-pty 端如何在一个 Web 服务里开出真 ShellShell 解析链配置 → 环境变量 → 登录 Shell服务端并不是随便启动一个bash。src/pty-manager.ts 中的defaultShell()按以下优先级解析用户显式配置的shell见 src/config.ts 的shell/shellArgs字段POSIX 下的$SHELL环境变量系统账户的登录 Shell比如 zsh兜底/bin/bash。Windows 侧则是DSH_SIDEBAR_SHELL→ PATH 或已知安装目录里的pwsh.exe优先 PowerShell 7→ 兜底powershell.exe。这样即使部署环境没设置SHELL打开的仍然是用户本人的登录 Shell而不是悄悄降级成 bash。启动时还有一个容易被忽视的细节POSIX 下 shell 以login shell-l方式拉起shellSpawnArgs()保证~/.profile、~/.zprofile等配置文件正常加载——这是真 Shell和一次性命令行的分水岭。spawn-helper 权限修复node-pty 的预编译二进制里带有一个spawn-helper辅助程序pnpm 安装时会剥掉它的可执行位导致所有终端都报posix_spawnp failed。src/pty-manager.ts 的ensureSpawnHelper()在插件激活时幂等地chmod 755修复它——这就是为什么安装脚本scripts/install.sh要专门处理构建权限。PtyManager终端会话的核心状态机src/pty-manager.ts 中的PtyManager是整个终端的调度中心每个终端用sessionId:tabId作为注册表键这决定了重连时如何找回同一个进程每个会话最多 N 个并发进程由terminalsPerSession配置默认 3超限直接返回pty-error进程比连接活得久WebSocket 断开不杀进程只有关标签页或插件卸载才会真正kill工作目录一致性重连时如果解析到的权威cwd和进程当初启动的cwd不一致页面加载竞态的产物会直接重开一个进程绝不让 Shell 停留在错误目录里。三种断开方式三种命运这是理解断线重连的关键。终端视图卸载时前端会区分三种情况发送不同的控制帧断开场景控制帧服务端行为用户关闭标签页{type:close}立即释放进程0ms 延迟用户切到其他会话{type:park}进程无限期挂起不启动重连倒计时切回来即续上页面刷新 / 崩溃 / 插件重建无帧裸断开启动reconnectGraceMs重连宽限期窗口内重连就复用原进程为什么要有park帧如果不区分会话切换和页面崩溃切换会话后那 30 秒宽限期一过就会把用户还在用的 Shell 杀掉。源码注释里把这叫做错误地杀掉了用户正在用的 Shell——正是 src/pty-manager.ts 花大段注释解释的动机。WebSocket 线路协议输入、缩放与回放客户端连接的是/sidebar/ws/terminal见 src/client/TerminalView.tsx服务端在 src/index.ts 的attachTerminal()中接线规则非常简洁上行普通文本帧 原始键盘输入JSON 帧{type:resize, cols, rows} 调整 pty 尺寸{type:close}/{type:park} 生命周期控制下行纯字符串帧 pty 原始输出流xterm.js 直接write背压保护发送前检查ws.bufferedAmount 4MB慢连接不会把服务端内存撑爆回放优先attachTerminal()接通后第一件事是ws.send(handle.transcript)——先把历史缓冲区整段发过去再订阅实时输出。这就是断线回放在协议层的落点。断线重连回放1MB 转录 30 秒宽限回放能力来自 src/pty-manager.ts 中的有界转录环transcript ring每个终端自启动起持续累积 pty 输出上限TRANSCRIPT_LIMIT 1 201MB超出时丢最旧的头部即使 Shell 已退出转录仍然保留可回放你会看到[process exited with code N]。配合默认30 秒重连宽限期reconnectGraceMs可在 src/config.ts 中调整完整链路是刷新页面 → 前端 xterm.js 组件重建 → 自动重连2 秒重试间隔服务端发现sessionId:tabId键对应的进程还活着 →不新开进程先整段回放 ≤1MB 的历史输出再续接实时流若超过宽限期才回来进程已被回收则透明地启动一个新 Shell。前端这一侧同样克制连续无原因失败达到 3 次才弹错误横幅服务端明确拒绝close code 1011则立即停止重试并显示原因横幅永不空转src/client/TerminalView.tsx。前端渲染xterm.js 的几个关键配置src/client/TerminalView.tsx 中的终端并非裸 xterm.js有几个值得一提的细节主题联动16 色 ANSI 调色板与应用的代码高亮同源one-dark / one-light见 src/client/one-dark-palette.ts明暗模式切换时原地重着色FitAddon rAF 合帧面板展开动画期间 ResizeObserver 每帧触发fit()被合帧到每动画帧一次避免测量抖动4000 行 scrollback 服务端 1MB 转录双保险覆盖常用回看范围URL 可点击pty 输出里的 http(s) 链接支持 CtrlClick 打开file://等方案被安全策略拒绝零尺寸保护容器高度为 0 时延迟open()靠 xterm.js 的 WriteBuffer 缓冲期间写入避免 WKWebView 下白屏崩溃。降级模式node-pty 不可用时的软着陆node-pty 是原生模块安装失败pnpm 11 的 strict-dep-builds、预编译下载失败等很常见。src/pty-deps.ts 的设计原则是绝不静态 import node-pty——否则一次加载失败会拖垮整个 Web 服务。改为惰性加载 缓存结果加载失败时插件保持挂载仅终端功能降级。前端收到 close code 1011 pty-deps-missing标记后通过/sidebar/api/terminal.deps拉取完整的可粘贴修复命令scripts/install.sh 或 scripts/install.ps1 的--repair模式横幅上一键复制、粘贴到本机执行即可修复。为什么不用 WS 直接带命令因为 WS close reason 上限只有 123 字节——这条约束在注释里写得明明白白。关键参数速查参数默认值作用terminalsPerSession3单会话最大并发终端数reconnectGraceMs30000裸断开后进程存活宽限期msshell/shellArgs自动解析覆盖 Shell 及其启动参数shellArgs非空时完全替换平台默认-l转录上限1MB每终端可回放的历史输出字节数以上均可在 src/config.ts 的SidebarConfig中配置更多细节见 README.md 与 docs/plans/2026-08-14-terminal-font-design.md、docs/plans/2026-08-26-pinned-terminal-design.md。小结DSH-better-sidebar 的浏览器终端之所以是真 Shell靠的是四件事各司其职node-pty在服务端持有真实的进程与 ptyWebSocket以极简协议原始输入 / resize / close / park双向泵数据xterm.js在前端完成 ANSI 渲染与自动重连PtyManager用sessionId:tabId键 1MB 有界转录 30 秒宽限期让刷新页面和关掉标签页有了截然不同的命运。想继续深挖推荐直接读 src/pty-manager.ts 的头部注释——它把整个生命周期设计讲得比本文更细。【免费下载链接】DSH-better-sidebar开放的侧边栏底座支持三方拓展注册新侧边栏页面。内置文件渲染编辑/终端/侧边对话/Git/子代理页面 Open sidebar foundation, supports third-party extensions to register new sidebar pages. Built-in file rendering/editing, terminal, side chat, Git, and sub-agent pages.项目地址: https://gitcode.com/gh_mirrors/ds/DSH-better-sidebar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
