Skip to content

feat: 真实 PTY resize 控制通道修复终端错位 - #104

Merged
yxsj245 merged 9 commits into
GSManagerXZ:mainfrom
sakuradairong:feat/terminal-real-resize-clean
Aug 4, 2026
Merged

feat: 真实 PTY resize 控制通道修复终端错位#104
yxsj245 merged 9 commits into
GSManagerXZ:mainfrom
sakuradairong:feat/terminal-real-resize-clean

Conversation

@sakuradairong

Copy link
Copy Markdown
Contributor

Summary

Supersedes #103 with a clean branch rebased from the latest upstream/main.

This PR keeps Xterm.js and fixes terminal misalignment by adding a real PTY resize path:

  • adds server-side PTY asset probing and control channel transports (POSIX FIFO / Windows named pipe)
  • wires typed terminal socket lifecycle and graceful shutdown handling
  • unifies the client terminal view around a single fit/resize observer and reporter
  • updates packaging, Docker, and installer paths for architecture-specific PTY assets
  • documents the terminal resize design and integration notes

Scope

Focused on the terminal misalignment / PTY resize issue. The previous fork-only file-deploy and Steam branch commits are not included in this clean PR branch.

Verification

  • cd server && npx tsc --noEmit
  • cd server && npx jest --runInBand — 5 suites / 40 tests passing
  • cd client && npx tsc --noEmit
  • node --check scripts/package.js
  • bash -n install-gsm3.sh
  • git diff --check upstream/main...HEAD

Not run in this rebuild pass:

  • browser end-to-end terminal verification
  • Linux ARM64 / Windows package runtime verification
  • full client Vitest baseline (known unrelated chunkUpload failures were observed earlier)

@yxsj245

yxsj245 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

/opencode 审查提交

@opencode-agent

opencode-agent Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR 审查报告: feat: 真实 PTY resize 控制通道修复终端错位

验证结果(全部通过)

检查项 结果
cd server && npx tsc --noEmit ✅ 通过
cd server && npx jest --runInBand ✅ 5 suites / 40 tests 通过
cd client && npx tsc --noEmit ✅ 通过
bash -n install-gsm3.sh ✅ 通过
node --check scripts/package.js ✅ 通过
尾部空白 / 语法检查 ✅ 干净

总体评价

架构整体质量很高。真实 PTY resize 控制通道(ptyControlChannel.ts)是本次的核心亮点——对 POSIX FIFO 做了 O_NOFOLLOW 打开、eUID 属主校验、0700 目录、祖先路径逐级校验、quarantine-rename 防 unlink 竞态清理,安全加固相当彻底,超过大多数开源项目同类实现。ptyAssets.ts 的固定 SHA-256 清单 + 跨架构打包(非本机资产只校验不执行)设计正确,CI matrix 打包逻辑也正确。InstanceManager 的 mutationChain 串行化 + 回滚 + durability barrier 是一大改进。客户端 resize 防抖/去重/尺寸校验做得干净,与服务器事件类型完全对齐。

以下按严重度列出发现的问题。

中高 — 需要处理

H1. .github/workflows/opencode.yml 意外回退(scope 外改动)
git diff 显示本 PR 将 .github/workflows/opencode.ymltimeout-minutes: 20 兜底超时删掉,并移除了生成的权限 allowlist config。原因:分支的基座提交 2563158("feat(opencode)")携带的是该文件的早期迭代,而 origin/main6709582(同名提交)是后来改进版。合并后会把 main 上的安全护栏回退掉。这与终端 resize 的 PR 范围无关,建议在合并前将该文件恢复为 origin/main 版本(或直接 re-base)。

中 — 建议修复

M1. 断开状态下关闭终端不发送 close-pty(绕过 N2-I2 队列设计)
client/src/pages/TerminalPage.tsx:717-721closeTerminalSessiondisconnected/reconnecting 状态只置 closing,不调用 requestCloseIfIdle;卸载清理(:536-539)的 needsGuardedClose 判定也不覆盖该状态(cleanupRequired/closeRequestInFlight 均为 false)。结果:断线时关闭标签页 → socket.ts 的排队重发机制完全绕过 → 服务端会话持续运行且无 cleanup 句柄。

M2. GameDeploymentPage 关闭残留终端时监听器泄漏 + 未 settle 路径
client/src/pages/GameDeploymentPage.tsx:3204-3237onPtyClosed/onTerminalError 挂在单例 socket 上,组件在关闭过程中卸载则监听器不清理,且会调用已卸载组件的 state setter;:3220retained 为 falsy 的 terminal-error 永远不 settle,按钮卡在"正在关闭"(当前服务端只发 retained:true,属潜在问题)。

M3. 优雅关闭时间预算偏紧
server/src/index.ts 强制退出预算 15s,而最坏路径为 scheduler destroy(≤5s)→ 实例优雅停止(≤10s)+ 有界关闭(≤4s)→ terminal cleanup(≤4s)→ final flush,串行最坏 ~19s。不响应实例可能在 final flush 前触发 15s 强杀,导致最终状态丢失。建议在 cleanup 阶段对实例强制走并行 closeTerminalInternal(跳过 10s 优雅等待)或放宽预算。

低 — 可选优化

  • L1 TerminalPage.tsx:1024lastReportedSize 是死字段(只写不读),可用作与服务器 terminal-resized ack 的对账。
  • L2 创建后立刻多发一次冗余 terminal-resizelastWrittenSizecreate-pty 时未预置),每个会话一次,无害。
  • L3 容器窄于 2 字符宽时 isValidTerminalSize 校验失败,creating 态无超时提示(依赖 ResizeObserver 再次触发才恢复)。
  • L4 creating/reconnecting 无超时兜底:若 create-ptyreconnect-session 既无 pty-created 也无 terminal-error,标签页永久挂起(服务端路径正常时不会发生,属健壮性缺口)。
  • L5 retainedTerminalSessionId 不持久化,刷新页面后 banner 消失(自愈但 UX 略差)。
  • L6 ptyAssets.tsisPtyBackupOwnerAlive 依赖 kill(pid,0) PID 存活探测,PID 复用场景下靠 30min stale 兜底,可接受。

未发现问题

  • 所有 /api 路由与 socket.io 均走认证中间件(index.ts:872-895 + authenticateToken),符合 AGENTS.md 规则 5/6。
  • 控制通道 FIFO 路径不存在路径穿越/符号链接风险(ptyControlChannel.ts 全链路防护)。
  • downloadAsset 使用 flags:'wx' 独占写 + 大小限制 + 重定向时剥离 Authorization,无 token 泄漏。
  • resize 尺寸严格校验(cols 2-1000 / rows 1-1000),客户端 50ms 防抖 + 同尺寸去重,无 resize 风暴。
  • 无遗留测试文件(符合 AGENTS.md 规则 18),无调试代码/console.log 噪音。

结论:除 H1(建议合并前处理 opencode.yml 回退)和 M1(建议补一个 requestCloseIfIdle 调用)外,其余均可接受。整体可以合并。

New%20session%20-%202026-08-04T07%3A12%3A14.011Z
opencode session  |  github run

@yxsj245

yxsj245 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@sakuradairong 优化下级别问题

@sakuradairong
sakuradairong force-pushed the feat/terminal-real-resize-clean branch from 757ec5b to aae0a01 Compare August 4, 2026 07:50
@sakuradairong

Copy link
Copy Markdown
Contributor Author

已按审查反馈处理并推送:a6b678e fix: address PR104 important review findings

处理项:

  • 持久化 failed-retainedretainedTerminalSessionId,刷新页面后仍能恢复清理入口;关闭成功后同步清除。
  • /api/terminal 挂载 authenticateToken,补齐认证要求。
  • Windows Named Pipe 控制通道增加 deadline 内重试,覆盖 pipe 尚未 ready 的瞬时 ENOENT / ECONNREFUSED / EBUSY / EAGAIN
  • closePty() 不再覆盖会话 owner socket,关闭结果继续通过 requester 机制通知。
  • POSIX detached PTY 关闭改为向进程组发送 SIGTERM / SIGKILL,失败再 fallback 到主进程。
  • Release 增加无版本 Linux 资产别名,安装脚本改用 curl -fL 并检查解压失败。
  • Docker 运行时资产预置移到 /root/server/builtin/data/lib,启动时补齐到 /root/server/data/lib,避免被数据卷遮蔽;相关文档已同步。

验证结果:

node .superpowers/reviews/pr104-important-check.mjs
→ pr104 important checks passed

cd server && npx tsc --noEmit
→ passed

cd server && npx jest --runInBand
→ 5 suites / 40 tests passed

cd client && npx tsc --noEmit
→ passed

bash -n install-gsm3.sh
bash -n start.sh
node --check scripts/package.js
git diff --check
→ passed

npm test
→ 5 suites / 40 tests passed

未运行:浏览器 E2E、Windows 实机、ARM64 实机。

@yxsj245

yxsj245 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

/opencode 审查修改 落实是否有效

Co-authored-by: yxsj245 <yxsj245@users.noreply.github.com>
@opencode-agent

opencode-agent Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

审查结论:修改已落实,整体有效

我检查了工作区(当前即 a6b678e),对比了审查报告中的 H1/M1/M2/M3 与 a6b678e 声称的修复项,并重新跑通了验证命令。

验证命令(本机复跑)

检查项 结果
cd server && npx tsc --noEmit ✅ 通过(安装依赖后)
cd server && npx jest --runInBand ✅ 5 suites / 40 tests
cd client && npx tsc --noEmit ✅ 通过
node --check scripts/package.js / bash -n install-gsm3.sh / bash -n start.sh ✅ 通过
git diff --check ✅ 干净

审查问题逐项落实情况

  • H1 opencode.yml 回退 ✅ 已解决。当前 merge-base 是改进后的 ec1b003git diff origin/main HEAD -- .github/ 只剩 build.yml 变更。
  • M1 断开态关闭不发送 close-pty ✅ 已修复。TerminalPage.tsx:717-722disconnected/reconnecting 分支现调用 requestCloseIfIdle,走 ACK-owned 队列,断线入队、重连重发。
  • M2 监听器泄漏 + 未 settle ✅ 已修复。卸载清理走 retainedTerminalCloseCleanupRef(GameDeploymentPage.tsx:249-254);onTerminalError 对匹配 sessionId 无条件 settleClose()retainedTerminalSessionId 已持久化到 localStorage 并刷新恢复(同时解决 L5)。
  • M3 关闭预算 ✅ 已修复。SHUTDOWN_FORCE_EXIT_TIMEOUT_MS = 30000(原 15s)。

a6b678e 声称项核实

  • /api/terminal 挂载 authenticateToken(index.ts:773)✅,ApiClient 请求拦截器已带 Bearer。
  • Windows 命名管道连接重试(ENOENT/ECONNREFUSED/EBUSY/EAGAIN,25ms 间隔)✅,受 deadline + runReadinessStage 兜底,不会挂死。
  • closePty 移除 target.socket = socket,ACK 改走 publicCloseRequester 机制(emitPublicCloseAck + publicCloseAckedIds 去重)✅,owner 与外部 requester 均恰好收到一次 pty-closed
  • POSIX 进程组信号(process.kill(-pid, ...) + 主进程 fallback)✅。
  • Release 无版本别名 + curl -fL / wget 下载失败与 tar 解压失败检查 ✅。
  • Docker 内置资产移到 /root/server/builtin/data/lib + start.sh cp -an 补齐 ✅,数据卷遮蔽问题已规避。

新发现(低严重度,不影响合并)

  • L-start.shstart.sh:49 的 PTY 预检路径从原来的 data/lib 改成 server/data/lib,非 Docker 安装布局(文件在 data/lib/)下该预检不再命中,只会打印"启动时自动下载"。因服务端运行时 getTargetDir 仍优先 data/libinstall-gsm3.shchmod -R 777,可自愈,无功能性影响;建议预检时同时尝试两个候选路径。
  • 作者声称运行的 node .superpowers/reviews/pr104-important-check.mjs 文件不在仓库中(应为临时审查脚本,符合规则 18 清理),无法复核,非阻塞。

结论:审查发现的中高级问题均已落实且实现正确,验证命令全部通过,可以合并。

New%20session%20-%202026-08-04T10%3A00%3A49.298Z
opencode session  |  github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants