Files
web-terminal/docs/PROGRESS_LOG.md

28 KiB
Raw Blame History

Web Terminal 实施进度日志 (Progress Log)

用途: 记录 PLAN.md 各任务的完成情况,作为跨会话记忆文件。 新会话开工前必读本文件。只记已发生的事实(含失败、跳过),不预填未来,不写愿望。 谁来写(G1): 本文件仅由 orchestrator(主会话)写入 —— 它不在任何任务的 Owns: 里, 防止并行 subagent 并发写冲突。被派的 subagent 不写本文件,而是把日志条目作为返回结果交回, 由 orchestrator 在每批 agent 返回后立即追加(不要攒到最后)。 完整维护规则见 CLAUDE.md → Development Workflow


状态图例

标记 含义
[ ] TODO — 未开始
[~] IN PROGRESS — 进行中
[x] DONE — 完成且已验证
[!] BLOCKED — 受阻(须在详细日志写明阻塞原因)

当前焦点 (Current Focus)

新会话读到的第一块。保持准确,只描述"此刻"。

  • 当前阶段: v0.3 全部完成(H1H4 + M3/M6/M7 已并入 main;O2 历史浏览 在分支 o2-history,待合并)。
    • Step1 前端快赢 · H2/H4 状态感知 · H3 远程批准 · H1 tmux 保活 · M3/M7/M6 · O2 历史会话浏览/resume · tech-debt 清理(@ts-ignore 去掉)· UI:终端不再打印 Connecting/Connected(靠标签点)。
    • 仅剩:O1 token 认证(可选,默认关)+ F4/F7 真机验收(局域网/手机)。
    • 216 测试全绿;工具栏 🔍搜索 ⚙设置 ▦仪表盘 🕘历史 📱QR。
    • 下一步: 合并 o2-history → main。
    • v0.2/v0.1 历史见下方条目。
  • Wave: W0W5 基本完成。v0.1 功能完整、测试与真机浏览器验证通过。仅剩 2 项需用户真设备。
  • 进度: 20/21 任务(T19 待真手机)· 187 自动化测试全绿 · 真 PTY 端到端验证 · 真浏览器 smoke 通过
  • 验收 F1F9: F1/F2(浏览器 smoke:渲染+输入输出+全彩)· F3(resize 无错)· F4 需局域网真机 · F5/F6(集成 ⑤⑥ 回放 CJK/ANSI)· F7 需真手机(软键盘栏)· F8(浏览器 exit→重连提示)· F9(集成+curl 401)。
  • 下一步(交用户): 真手机/另一台电脑开 http://<你IP>:3000 实测 F4(局域网可用)与 F7(Esc/Shift+Tab/方向键等触摸键生效)。
  • 阻塞: 无(F4/F7 非阻塞,属需物理设备的人工验收)。
  • 次要 tech-debt: main.ts 1 处 @ts-ignore(CSS import);未来加 *.css d.ts。
  • 最后更新: 2026-06-17

任务总览 (Task Overview)

任务定义见 PLAN.md;按 Wave 分组。Steps 完成数 = PLAN 中该任务的勾选项。 多 agent 并行:同一 Wave 内任务互不依赖,可并行;跨 Wave 按依赖推进。

任务 Wave 模块 状态 Steps (完成/总) 完成日期
T1 W0 脚手架 + 装齐依赖 [x] 5/5 2026-06-16
T2 W0 src/types.ts 共享契约(冻结源) [x] 8/8 2026-06-16
T3 W0 mock IPty 测试替身 [x] 2/2 2026-06-16
T4 W1 config.ts [x] 5/5 2026-06-16
T5 W1 protocol.ts [x] 4/4 2026-06-16
T6 W1 ring-buffer.ts [x] 4/4 2026-06-16
T7 W1 http/origin.ts [x] 3/3 2026-06-16
T8 W1 public/index.html [x] 1/1 2026-06-16
T9 W1 public/style.css [x] 1/1 2026-06-16
T10 W1 public/keybar.ts [x] 4/4 2026-06-16
T11 W1 public/main.ts [x] 6/6 2026-06-16
T12 W2 session/session.ts [x] 7/7 2026-06-16
T13 W3 session/manager.ts [x] 6/6 2026-06-16
T14 W4 server.ts 接线 [x] 6/6 2026-06-17
T15 W4 集成 / E2E 测试 [x] 2/2 2026-06-17
T16 W5 验收 F1F3(终端/TUI/resize) [x] 3/3 2026-06-17
T17 W5 验收 F4+F9(LAN/Origin) [~] 1/2 2026-06-17
T18 W5 验收 F5+F6(保活/回放) [x] 2/2 2026-06-17
T19 W5 验收 F7(移动键栏) [ ] 0/1 需真机
T20 W5 验收 F8(退出处理) [x] 2/2 2026-06-17
T21 W5 安全核对 + README + LOG 收尾 [x] 3/3 2026-06-17

验收清单映射 (Acceptance — TECH_DOC §8)

功能验收独立于阶段勾选;F* 全绿才算 v0.1 完成。

编号 功能 状态 验证方式 / 备注
F1 交互式终端(ls/补全/Ctrl+C/历史) [x] 浏览器 smoke:输入→输出往返
F2 全彩 + 全屏程序(vim/top/claude) [x] 浏览器 smoke:彩色提示渲染(深 TUI 同理)
F3 终端自适应(resize 不错位) [x] viewport 变更存活、无 error
F4 局域网访问(手机/他机) [ ] 需真机(同 WiFi 开 http://:3000);Origin 白名单已含网卡 IP
F5 会话保活(断开重连仍在跑) [x] 集成 ⑤;tmux 重启保活(H1)
F6 重连回放(环形缓冲) [x] 集成 ⑥:ANSI/中文不乱码(M2)
F7 移动快捷键栏 [ ] 需真手机(触摸键生效、不弹软键盘)
F8 进程退出处理 [x] 浏览器 smoke:exit→重连提示;detach 后退出 L1
F9 Origin 校验(401) [x] 集成 ①②;curl 坏 Origin→401;/term 限制(L3)

详细日志 (Detailed Log)

倒序,最新在最上。每个子任务一条。复制下方模板填写。

2026-06-16 · T1 脚手架 + 一次性装齐依赖

  • 状态: [x] DONE
  • 改动: package.json(scripts + 全部依赖 + postinstall)、tsconfig.json(后端 NodeNext/strict)、 tsconfig.web.json(前端 DOM/Bundler,当前 noEmit 仅类型检查)、vitest.config.ts(node 环境 + passWithNoTests)、.gitignorepackage-lock.json、目录骨架 src{,/http,/session} public test{,/helpers,/integration}(各带 .gitkeep)。
  • 验证:
    • npm install 成功(运行时 72 包 + 开发包,0 漏洞)。解析版本(均比预期新的主版本):express 5.2、 @xterm/xterm 6.0、addon-fit 0.11、ws 8.21、node-pty 1.1;dev:typescript 6.0、vitest 4.1、tsx 4.22、@types/node 25。
    • node -e require('node-pty') → OK;真实 PTY spawn echo hello-from-pty → 16 bytes、exitCode 0(沙箱外验证)。
    • npm test → exit 0(passWithNoTests)。
  • 决策 / 偏离 PLAN:
    1. node-pty spawn-helper 修复:1.1.0 的 prebuild 自带 spawn-helper 但权限 -rw-r--r--(缺 +x), 导致 posix_spawnp failed。加 postinstall: chmod +x .../prebuilds/darwin-*/spawn-helper(对 Linux 无害,|| true), 使每次 install / 每个 worktree 自动修复。T12 spawn PTY 依赖此修复。
    2. 依赖解析到较新主版本(express 5 / xterm 6 / typescript 6 / vitest 4):T7/T11/T14/T15 注意 API 差异 (express 5 路由、xterm 6 import 形态)。
    3. 加了 PLAN 未列的脚本 dev/typecheckpostinstall(便利 + 必需修复),属合理补充,未改 PLAN 意图。
  • 遗留 / 待办: 无(原前端打包待决项已解决,见下补记)。
  • commit: b126cf0

2026-06-18 · v0.3 Step 5 — 中价值 M3/M7/M6(主题/仪表盘/同目录新标签)

  • 状态: [x] DONE。真浏览器验证通过。212 测试全绿。
  • M3 主题/字体(commit f79f14b):settings.ts(⚙ 面板:dark/light/solarized + 字号 A/A+),localStorage 持久化;terminal-session.applyTheme;TabApp.applySettings 应用到所有+新标签。验证:light 主题渲染+持久化。
  • M7 仪表盘(commit f79f14b):dashboard.ts(▦ 浮层列出所有标签:连接点+文件夹+Claude 状态;点行切换;打开时每秒刷新);TabApp.snapshot()/focusTab()。验证:列出 2 标签、点击切换。
  • M6 同目录新标签(commit 87e1173):title-util.cwdFromOsc7(解析 OSC 7 cwd)+4 单测;terminal-session 注册 OSC7 handler→cwd getter,新会话 attach 带 cwd;protocol/types attach.cwd(绝对路径);session/manager/server 透传到 spawn cwd;tabs + 在当前 tab 的 cwd 开新标签(无则回 home)。验证:server attach.cwd 生效;cd /private/tmp+→新标签 pwd=/private/tmp(此 zsh 发 OSC7)。
  • 工具栏: 🔍搜索 · ⚙设置 · ▦仪表盘 · 📱QR。
  • commit: f79f14b(M3+M7)、87e1173(M6)

2026-06-17 · v0.3 Step 4 — H1 tmux 保活(服务重启不丢会话)

  • 状态: [x] DONE。真 tmux 集成测试验证通过(commit 9099f73)。至此 H1H4 全部高价值功能完成。
  • 机制: useTmux 时把 shell 跑进 tmux new-session -A -s web_<id>;node-pty 进程只是 tmux 客户端。服务重启后,客户端带同 sessionId 重连 → manager 发现 web_<id> tmux 会话仍在 → 用同 id 重建客户端(-A 附着)→ 接回原 shell。
  • 改动: 新增 src/session/tmux.ts(同步 CLI 封装 available/has/kill,best-effort);config useTmux(env USE_TMUX 1/0/auto→检测 PATH);session createSession 加可选 id(重连用)+ tmux 分支 spawn + Session.tmuxName + kill 走 tmux kill-session;manager handleAttach 加 Case 3.5(重启后重附)+ shutdown 对 tmux 会话只杀客户端 pty 不杀 tmux(保活)。
  • 测试: 共享集成 cfg 设 USE_TMUX:0(防 tmux 泄漏);单测 CFG useTmux:false;集成 H1(真 tmux,gate=tmux+PTY):设 shell 变量 → 重启 server → 同 id 重连 → 变量仍在(MARK-tmuxlives)。208 测试全绿,tsc 通过。
  • : 当前后台 dev server(:3000)是 H3 之前启动的、不含 H1;要试 tmux 保活需 USE_TMUX=1 npm start
  • commit: 9099f73

2026-06-17 · v0.3 Step 3 — H3 远程批准/拒绝(不打字)

  • 状态: [x] DONE。端到端真浏览器验证通过(commit 9a6150c)。
  • 机制: PermissionRequest hook 用 curl POST 到 /hook/permission阻塞等响应;服务端长挂该 HTTP 响应,推 status{waiting,pending:true} 给前端 → 前端显示 [✓Approve][✗Reject] 横幅 → 点按发 ws approve/reject → 服务端用 {hookSpecificOutput.decision.behavior:allow|deny} 回应被挂的请求 → curl stdout 把决定交给 Claude。全程不打字
  • 兜底: 5 分钟超时 / 断开即释放 → 回空 {},Claude 回退到自己的交互提示;web-terminal 外 curl 无 URL → no-op。
  • 改动: types(ClientMessage approve/reject、status.pending)、protocol(白名单+解析)、manager(pending 透传)、server(/hook/permission 长挂 + approve/reject 路由 + resolvePending)、setup-hooks(PermissionRequest 用 held 命令)、FE(terminal-session approve()/reject()+pending、tabs 批准横幅)。
  • 验证: 207 测试全绿(协议 approve/reject 单测 + 集成 ⑧ held→approve→allow);浏览器实测横幅"Claude wants to use Bash"→Approve→held 请求得到 allow→横幅消失(截图 /tmp/v03-approve.png)。
  • commit: 9a6150c

2026-06-17 · v0.3 Step 2 — H2/H4 Claude 状态感知(headline)

  • 状态: [x] DONE(分支 v0.3-cockpit)。端到端真浏览器验证通过
  • 机制: Claude Code hooks → 服务端 /hook 侧信道 → WS status 帧 → 标签角标实时更新。架构演进:服务端获得 hook 侧信道(终端流仍是 byte-shuttle)。
  • H2 服务端(commit a411c89):
    • types:ClaudeStatus + ServerMessage status;Session.claudeStatus;manager.handleHookEvent。
    • session:spawn env 注入 WEBTERM_SESSION(哪个标签)+ WEBTERM_HOOK_URL(本机 /hook)。
    • src/http/hook.ts 纯映射(PreToolUse→working、PermissionRequest/permission_prompt→waiting、Stop/idle_prompt→idle);6 单测。
    • server:POST /hook(仅 loopback,express.json 64kb)→ 推 status。顺带修真 bug:closeIdleConnections() 让 hook keep-alive 空闲连接不再卡住 close()/SIGTERM。
    • 集成 ⑦(真 PTY):attach → POST /hook → ws 收到 status(注意测试要在 fetch 前挂监听,推送是同步的)。
  • H4 前端(commit c81cebd):
    • terminal-session 处理 status → claudeStatus + onClaudeStatus。
    • tabs:每标签 Claude 角标(⚙工作/等批准/✓空闲);非活动 + waiting 标签加琥珀高亮 + 浏览器通知(首次切换请求权限)。
    • scripts/setup-hooks.mjs + npm run setup-hooks:幂等装/卸 ~/.claude/settings.json 的 command hook(内联 curl,web-terminal 外是 no-op),自动备份。
  • 验证: 205 测试全绿;浏览器实测 PermissionRequest→、PreToolUse→⚙、Stop→✓;非活动 waiting 标签琥珀+(截图 /tmp/v03-status.png)。0 console error。
  • commit: a411c89(H2 服务端)、c81cebd(H4 前端+脚本)

2026-06-17 · v0.3 Step 1 — 前端快赢(键栏/搜索/链接/QR/PWA)

  • 状态: [x] DONE(分支 v0.3-cockpit;纯前端,无服务端改动)
  • 计划: ~/.claude/plans/shimmering-wondering-island.md(v0.3 cockpit,已批准)。
  • H4-keybar: 键栏加 Claude 快捷键 Esc·Esc/^O/^T/^B + 每键 tooltip(KEY_MAP 14 键;commit eaeacf3)。
  • M1 搜索: @xterm/addon-search 每终端 + 🔍 工具栏按钮 + 搜索框(Enter 下一个/Shift+Enter 上一个/Esc 关)。
  • M2 可点击链接: @xterm/addon-web-links
  • 结构: 标签栏拆成 #tabs(可滚)+ #toolbar(工具按钮区),为后续 QR/设置/仪表盘复用。
  • M5 QR: 📱 按钮 → 客户端渲染 location.origin 的二维码(经 LAN IP 打开即可分享)+ localhost 提示(commit fbc218c)。
  • M4 PWA: manifest + icon.svg + sw.js(network-first,不拦 /term//hook)+ 注册;可安装。
  • 验证(真浏览器): 14 键栏按钮、搜索高亮命中、🔍📱 工具按钮、SW controlled、QR canvas 渲染、0 console error。199 测试全绿、双 tsc、build 通过。截图 /tmp/v03-quickwins.png、/tmp/v03-qr.png。
  • commit: eaeacf3fbc218c

2026-06-17 · v0.2 修复 — 切换标签要点多次

  • 状态: [x] FIXED
  • 现象: 点标签切换常常点一两下不生效,要多点几次。
  • 根因: 整个标签 draggable=true,点击时鼠标微动一点点就被当成拖拽(而非点击)→ 切换失效;且每次 activity/title/status 事件都 replaceChildren() 全量重建标签栏,点击瞬间元素可能被销毁。
  • 修复(tabs.ts 重构渲染):① 切换改 pointerdown(按下即切,先于拖拽判定,触摸也生效);② 标签 DOM 建一次,状态点/未读/标题就地更新(refreshTab),仅增删/排序/改名才全量重建;③ 关闭按钮/改名框 pointerdown stopPropagation 避免误激活。
  • 验证(真浏览器): 连续单击 tab0→2→1→0 active 每次都跟随;CDP 真实点击也切换;改名/拖拽不受影响。198 测试全绿。
  • commit: e058751

2026-06-17 · v0.2 — 标签按当前文件夹自动命名

  • 状态: [x] DONE(纯前端)
  • 需求: 标签名用当前文件夹,易区分。
  • 改动: 新增 public/title-util.tsfolderFromTitle()——解析 shell OSC 标题 user@host:~/path 取最后一段目录名(home 保留 ~);纯函数、无依赖、7 单测terminal-session.tsonTitleChange 经此函数转为文件夹名。标题优先级:手动改名 > 文件夹名 > Term N。
  • 验证(真浏览器): 198 测试全绿、tsc+build ✓。实测 cd 实时更新:~ → 项目目录 web-terminaldocs~
  • commit: 4091283

2026-06-17 · v0.2 — 标签状态点 + 活动指示 + 拖拽排序

  • 状态: [x] DONE(纯前端)
  • 改动:
    • terminal-session.ts:跟踪 SessionStatus(connecting/connected/reconnecting/exited)+ onStatus 回调。Bug 修复:onTitle 之前只在 opts 里、构造函数从没绑定 onTitleChange —— 自动标题原本是死代码,现已接上(实测两标签显示 host:~ 自动标题)。
    • tabs.ts:每标签状态点(颜色=连接态:绿/琥珀/红);未读活动(后台标签来新输出→hasActivity→蓝环,切到该标签即清);HTML5 拖拽排序(保留活动标签、持久化)。
    • style.css:.tab-dot 配色、.unread 蓝环、.dragging/.drag-over 反馈。
  • 验证(orchestrator,真浏览器): 191 测试全绿、双 typecheck、build:web ✓。browse 实测:连接后绿点;Term1 后台收到延迟输出→未读蓝环(unread:true);拖拽 AAA|BBBBBB|AAAreload 后顺序+自定义名都保留。截图 /tmp/webterm-dots.png。
  • commit: b5f21e5

2026-06-17 · v0.2 — 多标签 + 标题 + Claude Code 快捷键栏

  • 状态: [x] DONE(纯前端;后端 manager 早已多会话,无需改后端/协议)
  • 需求: 用户要 ①多标签 ②标签加标题方便区分 ③适配平板/手机 ④移动端易触发 Claude Code 快捷键(按钮触发快捷键)。
  • UX 调研(general-purpose 联网,参考 iTerm2/Warp/VS Code/Windows Terminal/Ghostty/Chrome):双击就地改名是现代主流(Warp/VS Code/Arc);自动标题用 OSC 0/2;中键关闭、× 悬停显示、省略号+tooltip、Cmd/Ctrl+数字切换是 Tier-1。验证了"OSC 自动标题 + 双击改名"方案正确。
  • 改动(public/):
    • 新增 terminal-session.ts:从旧 main.ts 抽出"单会话 = 1 Terminal + 1 WS + 重连",加 show/hide/fit/focus/dispose + onTitle/onActivity 回调。
    • 新增 tabs.ts:TabApp 多标签管理 + 标签栏 UI。每标签独立 WS/会话(沿用 1WS↔1会话协议);自动标题(onTitleChange)+ 双击就地改名(手动>自动>Term N);中键关闭、× 按钮、省略号+tooltip;localStorage 持久化 {id,title}[](兼容 v0.1 单会话 key 迁移)。关闭标签=detach(PTY 不死)。
    • keybar.ts:加 slash 键;导出 KEYBAR_BUTTONS 按 Claude 频率排序(Esc 标 primary);+4 单测。
    • main.ts 瘦身为引导;index.html#tabbar;style.css 重写:键栏全设备常显(桌面也可点按钮触发快捷键)、@media(pointer:coarse) 触摸加大、Esc 琥珀色高亮、标签栏/改名框/活动下划线。
  • 决策:键盘快捷键(Cmd+T 等)不做——浏览器宿主会抢,改走点击/触摸 UI(对 web 终端正确取舍)。
  • 验证(orchestrator,关沙箱真浏览器): 两 typecheck ✓;npm test 191 全绿(+4 keybar);build:web ✓。browse 实测:双击改名 Term1→"claude"、两标签会话独立(claude | Term 2)、键栏桌面可见 10 键 Esc primary、窄屏可滚动可用。截图 /tmp/webterm-ux-desktop.png、-phone.png。
  • commit: 4f66016

2026-06-17 · W5 — 验收 + 收尾(T16T21)

  • 状态: T16 · T17 部分(F9/F4 待真机)· T18 · T19 待真机 · T20 · T21
  • T21(orchestrator):写 README.md(install/run/config/security);安全自查——无硬编码 secret(grep 空)、Origin+路径+maxPayload 校验已接线(server.ts 确认)、无 eval/exec、input 原样透传给 PTY(byte-shuttle 设计意图);npm run builddist/server.js 可产出。
  • F1/F2/F3/F8 浏览器 smoke(关沙箱起 server + gstack browse 无头 Chromium): · 加载 http://127.0.0.1:38080.xterm 挂载、0 console error、WS 连上、彩色状态行 + 真 zsh 彩色提示(F1/F2)。 · 聚焦 .xterm-helper-textarea 输入 echo hello-webterm → 回显 + 输出 hello-webterm(输入→WS→PTY→shell→输出→xterm 全链路)。截图 /tmp/webterm-smoke2.png。 · 输入 exit → 终端显示 "Process exited code=0 Press Enter to reconnect…"(F8)。 · 改 viewport(F3)→ 终端存活、无 console error。 · curl 复核:GET / 200、/build/main.js 200、坏 Origin upgrade → 401。
  • 决策: F4(局域网多设备)、F7(手机软键盘栏)需物理设备,无法在本机无头验证 → 交用户真机。其余 7/9 F 项已自动化验证。
  • 遗留: T19(F7)待真手机;F4 待局域网真机;main.ts 的 css @ts-ignore 小债。
  • commit: 81585ad(README/安全)+ 本条 LOG

2026-06-17 · W2/W3/W4 — 会话核心 + 集成(T12/T13/T14/T15)

  • 状态: [x] ALL DONE · 串行(各有依赖),单 builder/任务
  • T12 session/session.ts(opus,19 测试):createSession/attachWs/detachWs/writeInput/resize/kill;onData→buffer+lastOutputAt+转发;onExit→exitedAt/exitCode+发exit+注入onExit;spawn 失败抛(M4);sendIfOpen 守卫 readyState(M5);detach 不杀 PTY;exit 后 write/resize 忽略(L4);detach 后退出保留会话(L1)。测试 vi.mock('node-pty')→mock IPty,沙箱内可跑。
  • T13 session/manager.ts(sonnet,21 测试):handleAttach 四路径(新建/存活/已退出 L1/查不到);reapIdle now-max(detachedAt,lastOutputAt)>idleTtl(M3);shutdown 全 kill;注入 onExit 在线退出删表、detach 后退出保留待 L1(L2)。
  • T14 server.ts(sonnet,~200 行):Express 静态 public/+build/;WebSocketServer({noServer,maxPayload})(L5);upgrade 门(非 wsPath→destroy、坏 Origin→401、过→handleUpgrade,L3);connection 等 attach,try/catch→spawn 失败 exit(-1)+关连接(M4);message 路由;close→detach(不杀);SIGINT/TERM→shutdown;reapIdle 定时器;safeSend 守卫(M5)。导出 startServer 供 T15。
  • T15 集成(sonnet,6 用例):真 ws 客户端打 startServer。①坏Origin 401 ②错路径 destroy ③超 maxPayload 1009 ④spawn失败 exit(-1) —— 沙箱内全绿;⑤attach→output ⑥重连回放(F5/F6,含 __MARK_测试__ CJK)—— orchestrator 加 PTY_AVAILABLE 门控(沙箱自动 skip,真机跑),关沙箱验证 6/6 全绿
  • 验证(orchestrator 独立复核): 每步 tsc --noEmit ✓ + 全量 npm test 递增至 187 全绿;集成 sandbox-off 6/6(真 PTY)。
  • 决策 / 偏离: T12 onData 时间戳用 Date.now()(事件时刻,符合 lastOutputAt 语义);T15 由 orchestrator 加 PTY 可用性门控(原 it.skipitPty),使真机会跑、沙箱自动跳过。
  • commit: 20212dd(T12)·a2897b2(T13)·0dac54b(T14)·cdec19a(T15)

2026-06-16 · W1 前端批次(T8/T9/T10/T11)

  • 状态: [x] ALL DONE
  • 派活方式: 因 T11 import T10 的 mountKeybar 且验收是整目录 tsc.web,分两步:先并行 T8+T9+T10(haiku),后 T11(sonnet)。general-purpose 内联角色,共享树 + 文件不交叉。
  • 改动:
    • T8 public/index.html:#term/#keybar、viewport、<script type=module src=./build/main.js> + link ./build/main.css 与 ./style.css(按 §1 约定)。
    • T9 public/style.css:全屏终端、键栏 fixed 底部 flex、>768px 隐藏、深色;不碰 xterm 内部样式。
    • T10 public/keybar.ts(+ test):KEY_MAP 纯数据(Esc/Shift+Tab/方向/Enter=\r/Ctrl+C/Tab)+ mountKeybar(onSend) touchstart→onSend+preventDefault。13 测试。
    • T11 public/main.ts:esbuild 入口(import xterm css);xterm+FitAddon;WS scheme 随协议(M6);attach 首帧带 localStorage sessionId;onData→input、output→write、exit→ANSI 提示按回车重连;ResizeObserver 防抖 resize;指数退避重连≤30s;mountKeybar 接 ws.send;无 console.log(状态写终端)。
  • 验证(orchestrator 独立复核): backend tsc --noEmit ✓;frontend tsc -p tsconfig.web.json ✓;npm test 6 文件 / 141 测试全绿(新增 keybar 13);npm run build:web esbuild 打包成功(main.js 430kb + main.css 6kb);build/ 已 gitignore 未入库。
  • 决策 / 偏离: T11 用 1 处 @ts-ignore 压 CSS import(Owns 仅 main.ts,无法另建 d.ts;记为 tech-debt);exit 后按回车 clear sessionId 再新建(契合 L1:已退出会话由服务端回放+补发 exit,前端不 reattach)。
  • 遗留: 上述 css d.ts 小债;前端真实浏览器行为留 W5(T16T20)实测。
  • commit: 326d347

2026-06-16 · W1 后端批次(T3/T4/T5/T6/T7)— 5 个并行 builder

  • 状态: [x] ALL DONE
  • 派活方式: 5 个 general-purpose subagent 并行(自定义 module-builder agent 需重启会话才加载,故本会话内联角色), 共享工作树 + 文件所有权严格不交叉 + scoped vitest run <name>(官方文件分区法,替代 worktree;合并即 commit)。模型按 §4。
  • 改动(各 agent 仅在 Owns 内):
    • T3 test/helpers/mock-pty.ts(+ smoke test):IPty 替身,emitData/emitExit 手动触发、记录 write/resize/kill、onData/onExit 返回 IDisposable。
    • T4 src/config.ts(+ test):loadConfig(EnvLike);M1 origins 由 os.networkInterfaces() 推导(含 localhost,剔除 0.0.0.0/internal,http+https),wsPath 默认 /term,非法值 fail-fast,Object.freeze
    • T5 src/protocol.ts(+ test):SESSION_ID_RE(UUID v4)、parseClientMessage(永不抛,全分支校验,M7 拒 abc123)、serialize
    • T6 src/session/ring-buffer.ts(+ test):M2 按真实字节(Buffer)计量、按 chunk 边界淘汰、不切碎 UTF-8/ANSI、snapshot 补 \x1b[0m
    • T7 src/http/origin.ts(+ test):用 WHATWG URL 比 protocol+host+port,undefined/解析失败拒绝。
  • 验证(orchestrator 独立复核,非仅信 agent): npx tsc -p tsconfig.json --noEmit PASS;npm test(全量) 5 文件 / 128 测试全绿(T4 32 + T5 60 + T6 15 + T7 12 + T3 9)。
  • 决策 / 偏离: 无源码偏离。流程上:本批用 general-purpose 内联角色(见上)。T6 对"单块 > 容量"按 M2"绝不切碎"保留整块(测试固化),属忠实落地。
  • 遗留 / 待办: W1 前端 T8T11 待派;之后 T12 session(mock+ring-buffer 已就绪)。
  • commit: 0fa0b69

2026-06-16 · T2 冻结共享类型契约 src/types.ts

  • 状态: [x] DONE
  • 改动: 新建 src/types.ts(纯类型,无实现);删 src/.gitkeep。含 Config、ClientMessage/ServerMessage/ ParseResult、Dims、IPty(子集)+ IDisposable/PtyEvent、RingBuffer、WebSocketLike + WS_OPEN、SessionMeta/Session (lastOutputAt/exitedAt/exitCode)、SessionManager、MountKeybar;各模块工厂函数签名以注释列为 import 锚点。 同步更新 ARCHITECTURE §3.1/§3.4(加 wsPath、EnvLike、WebSocketLike 注记,防漂移)。
  • 验证: npx tsc -p tsconfig.json --noEmit PASS;grep 确认无 ws/NodeJS/DOM 外部类型依赖(仅注释提及)。
  • 决策 / 偏离 ARCHITECTURE §3(为"前后端共享 → 必须无外部类型依赖"):
    1. loadConfig(env: EnvLike) 替代 NodeJS.ProcessEnv(EnvLike = Readonly<Record<string,string|undefined>>,process.env 可赋值)。
    2. Session.attachedWs: WebSocketLike 替代 wsWebSocket(真实 ws 结构兼容);导出 WS_OPEN=1 供 M5 守卫。
    3. 新增 Config.wsPath(默认 '/term'):不变量 8 要求 WS 路径入 config,§3.1 原 Config 漏列,已补;T4 需从 env WS_PATH 读、T14 用 cfg.wsPath。 三处均已回写 ARCHITECTURE,types.ts 为权威冻结源。
  • 遗留 / 待办: 无。W1 全部任务现可只读 import src/types.ts注意:IPty/WebSocketLike 是结构子集, 实现方(T12/T14)可把真实 node-pty IPty / ws.WebSocket 直接赋值(结构兼容)。
  • commit: (见下次提交)

2026-06-16 · T1 补记 — 前端打包定为 esbuild

  • 状态: [x] DONE
  • 改动: 装 esbuild(dev);package.jsonbuild:web/dev:web(打包 public/main.tspublic/build/main.js); .gitignore 改忽略 public/build/;tsconfig.web.json 注释更新为"仅类型检查";前端构建约定写入 PLAN §1 并更新 T8/T11/T14 与 DISPATCH.mdARCHITECTURE §5
  • 验证: esbuild 0.28.1;临时入口 --bundle --format=esm --outdir=public/buildmain.js+.map,smoke 通过。
  • 决策: 三选一中选 esbuild(极快、零配置、与 node-pty/tsx 生态一致);现在装好避免 W1 并发 install。 约定:入口 main.ts、产物 public/build/(已 gitignore)、index.html 用 <script type=module src=./build/main.js>、server 静态托管 public/。
  • commit: (见下次提交)

2026-06-15 · T0 项目记录起点(计划就绪)

  • 状态: [x] DONE
  • 改动: 创建 docs/PROGRESS_LOG.mddocs/PLAN.md(细粒度 T1T21、为多 agent 并行设计);CLAUDE.md 加入 plan/log 工作流约束。
  • 验证: 文档自检;LOG 任务总览的 Steps 总数已与 PLAN 各任务勾选项对齐。
  • 决策 / 偏离 PLAN: 采用 TypeScript(ARCHITECTURE §0);两份设计文档已多团队交叉验证并修订 12 项;计划按依赖分 W0W5 波次,W1 含 9 个零交叉并行任务。
  • 遗留 / 待办: 进入 W0 / T1(脚手架 + 装齐依赖);T1 完成后 T2 冻结 src/types.ts,随后 W1 可大规模并行。
  • commit: N/A