Files
web-terminal/docs/PROGRESS_LOG.md
2026-06-17 15:38:09 +02:00

248 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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**: **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](./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/历史) | `[ ]` | |
| 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.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 — 验收 + 收尾(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 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(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` 替代 `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`(细粒度 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