296 lines
25 KiB
Markdown
296 lines
25 KiB
Markdown
# Web Terminal 实施进度日志 (Progress Log)
|
||
|
||
> **用途**: 记录 [PLAN.md](./PLAN.md) 各任务的完成情况,作为**跨会话记忆文件**。
|
||
> 新会话开工前**必读本文件**。只记**已发生的事实**(含失败、跳过),不预填未来,不写愿望。
|
||
> **谁来写(G1)**: 本文件**仅由 orchestrator(主会话)写入** —— 它不在任何任务的 `Owns:` 里,
|
||
> 防止并行 subagent 并发写冲突。被派的 subagent **不写本文件**,而是把日志条目作为返回结果交回,
|
||
> 由 orchestrator 在每批 agent 返回后**立即追加**(不要攒到最后)。
|
||
> 完整维护规则见 [CLAUDE.md → Development Workflow](../CLAUDE.md)。
|
||
|
||
---
|
||
|
||
## 状态图例
|
||
|
||
| 标记 | 含义 |
|
||
|------|------|
|
||
| `[ ]` | TODO — 未开始 |
|
||
| `[~]` | IN PROGRESS — 进行中 |
|
||
| `[x]` | DONE — 完成且已验证 |
|
||
| `[!]` | BLOCKED — 受阻(须在详细日志写明阻塞原因) |
|
||
|
||
---
|
||
|
||
## 当前焦点 (Current Focus)
|
||
|
||
> 新会话读到的第一块。保持准确,只描述"此刻"。
|
||
|
||
- **当前阶段**: **v0.2 进行中** —— 多标签 + 标题 + 移动端 Claude Code 快捷键栏(✅ 已做,见下方 v0.2 条目)。v0.1 见下。
|
||
- **Wave**: **W0–W5 基本完成**。v0.1 功能完整、测试与真机浏览器验证通过。仅剩 2 项需用户真设备。
|
||
- **进度**: **20/21 任务**(T19 待真手机)· **187 自动化测试全绿** · 真 PTY 端到端验证 · **真浏览器 smoke 通过**。
|
||
- **验收 F1–F9**: ✅ 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](./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 | 验收 F1–F3(终端/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/历史) | `[ ]` | |
|
||
| F2 | 全彩 + 全屏程序(vim/top/claude) | `[ ]` | |
|
||
| F3 | 终端自适应(resize 不错位) | `[ ]` | |
|
||
| F4 | 局域网访问(手机/他机) | `[ ]` | 与 F9 联测,确认 Origin 白名单含网卡 IP |
|
||
| F5 | 会话保活(断开重连仍在跑) | `[ ]` | |
|
||
| F6 | 重连回放(环形缓冲) | `[ ]` | 验证 ANSI/中文不乱码(M2) |
|
||
| F7 | 移动快捷键栏 | `[ ]` | |
|
||
| F8 | 进程退出处理 | `[ ]` | 含 detach 后退出补发 exit(L1) |
|
||
| F9 | Origin 校验(401) | `[ ]` | 含 `/term` 路径限制(L3) |
|
||
|
||
---
|
||
|
||
## 详细日志 (Detailed Log)
|
||
|
||
> **倒序**,最新在最上。每个子任务一条。复制下方模板填写。
|
||
|
||
<!-- ========== 条目模板(复制此块,删除注释) ==========
|
||
### YYYY-MM-DD · S?.? <子任务标题>
|
||
|
||
- **状态**: `[x]` DONE / `[~]` IN PROGRESS / `[!]` BLOCKED
|
||
- **改动**: <文件:函数/范围,如 src/protocol.ts:parseClientMessage、test/protocol.test.ts>
|
||
- **验证**: <跑了什么命令 + 结果。如 `npm test` → 12 passed;或贴关键断言>
|
||
- **决策 / 偏离 PLAN**: <无 / 为何偏离 + 是否已回写 PLAN.md>
|
||
- **遗留 / 待办**: <无 / 后续子任务需注意的点>
|
||
- **阻塞**(仅 `[!]` 时): <卡在哪,需要什么>
|
||
- **commit**: <hash 或 N/A>
|
||
======================================================= -->
|
||
|
||
### 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`)、`.gitignore`、`package-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`/`typecheck` 与 `postinstall`(便利 + 必需修复),属合理补充,未改 PLAN 意图。
|
||
- **遗留 / 待办**: 无(原前端打包待决项已解决,见下补记)。
|
||
- **commit**: `b126cf0`
|
||
|
||
### 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**: `eaeacf3`、`fbc218c`
|
||
|
||
### 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.ts` 的 `folderFromTitle()`——解析 shell OSC 标题 `user@host:~/path` 取最后一段目录名(home 保留 `~`);纯函数、无依赖、**7 单测**。`terminal-session.ts` 把 `onTitleChange` 经此函数转为文件夹名。标题优先级:手动改名 > 文件夹名 > Term N。
|
||
- **验证(真浏览器)**: 198 测试全绿、tsc+build ✓。实测 `cd` 实时更新:`~` → 项目目录 `web-terminal` → `docs` → `~`。
|
||
- **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|BBB`→`BBB|AAA` 且**reload 后顺序+自定义名都保留**。截图 /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 — 验收 + 收尾(T16–T21)
|
||
|
||
- **状态**: 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 build` → `dist/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.skip` → `itPty`),使真机会跑、沙箱自动跳过。
|
||
- **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(T16–T20)实测。
|
||
- **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 前端 T8–T11 待派;之后 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` 替代 `ws` 的 `WebSocket`(真实 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.json` 接 `build:web`/`dev:web`(打包 `public/main.ts` → `public/build/main.js`);
|
||
`.gitignore` 改忽略 `public/build/`;`tsconfig.web.json` 注释更新为"仅类型检查";前端构建约定写入
|
||
`PLAN §1` 并更新 T8/T11/T14 与 `DISPATCH.md`、`ARCHITECTURE §5`。
|
||
- **验证**: esbuild 0.28.1;临时入口 `--bundle --format=esm --outdir=public/build` 出 `main.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.md`、`docs/PLAN.md`(细粒度 T1–T21、为多 agent 并行设计);`CLAUDE.md` 加入 plan/log 工作流约束。
|
||
- **验证**: 文档自检;LOG 任务总览的 Steps 总数已与 PLAN 各任务勾选项对齐。
|
||
- **决策 / 偏离 PLAN**: 采用 TypeScript(ARCHITECTURE §0);两份设计文档已多团队交叉验证并修订 12 项;计划按依赖分 W0–W5 波次,W1 含 9 个零交叉并行任务。
|
||
- **遗留 / 待办**: 进入 **W0 / T1**(脚手架 + 装齐依赖);T1 完成后 T2 冻结 `src/types.ts`,随后 W1 可大规模并行。
|
||
- **commit**: N/A
|