# Phase 2 · TikTok 采集层 · Implementation Plan (技术大纲)

> 详细 step-by-step 在每个 task 内由执行 agent 按 TDD 写。

**Goal:** 把 TikTok WebCast 协议封装成可被多个 phase 复用的底层基础包：tiktok-live-connector 封装 + Electron BrowserWindow 桥（替代 Playwright，因为 catclaw 已经在用这条线）+ Cookie 加密存储 + JSONL 录制器。

**Architecture:** 4 个核心模块的薄封装，本 phase **不**写业务逻辑，仅出"协议层基础设施"。

**Tech Stack:** tiktok-live-connector + Electron BrowserWindow + node:worker_threads + macOS Keychain (via keytar)

---

## File Structure

```
packages/core/src/tiktok-engine/
  ├── live-monitor.ts         (Phase 7 实装，本 phase 仅出框架接口)
  ├── live-monitor-worker.ts  (worker thread 入口)
  ├── manager.ts              (LiveMonitorManager 多房协调，本 phase 出基础类)
  └── types.ts                (24 种事件类型)

packages/core/src/playwright-engine/
  ├── pool.ts                 (Electron BrowserWindow 池，1-3 个并发)
  ├── route.ts                (Route One/Two 切换：弹窗式 vs hidden)
  ├── cookie-store.ts         (macOS Keychain 加密存 cookie)
  └── tasks/                  (空目录，phase 4-6 才填具体任务)

packages/core/src/sign-fetcher/
  └── index.ts                (从 hidden BrowserWindow 抓 X-Bogus 等签名给 tiktok-live-connector)
```

---

## Task 概要（按 TDD 节奏）

| # | 主题 | 关键文件 | 关键代码 sketch |
|---|---|---|---|
| 1 | tiktok-live-connector 装包 + 起一个 hello-world 连接 | `live-monitor.ts:RoomConnection` | `new TikTokLiveConnection(uniqueId).connect()` |
| 2 | 24 种事件类型 EVENT_MAP 映射 + NormalizedEvent 类型 | `types.ts` | 见 Phase 7 plan Task 1 |
| 3 | 自动重连（5x 指数退避 3-48s） | `live-monitor.ts:handleDisconnect` | setTimeout + retry counter |
| 4 | event-extractor.ts（提取 user 字段） | (Phase 7 已迁移过) | catclaw `live-monitor.ts:17-64` |
| 5 | LiveMonitorManager（worker thread 池） | `manager.ts` | `new Worker('./worker.ts', { workerData: { uniqueId } })` |
| 6 | Electron BrowserWindow 池 | `playwright-engine/pool.ts` | 同时最多 3 窗口，per task 拿一个 |
| 7 | Sign fetcher（抓 X-Bogus / X-Tt-Params） | `sign-fetcher/index.ts` | hidden BrowserWindow + bridge.executeJS 注入 navigator hooks |
| 8 | Cookie 加密存储 | `cookie-store.ts` | keytar.setPassword('pawcast', 'tiktok-cookie-' + accountId, encrypted) |
| 9 | JSONL Recorder（每 room 一个文件 +`~/.pawcast/recordings/<uniqueId>-<date>.jsonl`） | `recorder.ts` | append-only stream，每 1000 行 flush |
| 10 | Replayer（读 JSONL 喂回 NormalizedEvent stream，方便测试） | `replayer.ts` | for line of jsonl: emit(parse(line)) |

---

## 准入条件

- ✅ Phase 1 已 merge

## 准出条件

- ✅ 输入一个真实 @username（用 testing 账号 @link8_kr），实时收到 gift / chat / like 事件，写入 `~/.pawcast/recordings/`
- ✅ 同时连 8 个房间不卡顿（CPU < 30%, RAM < 500MB）
- ✅ Cookie 加密能存能读
- ✅ 录制 1 小时事件流，回放工具能复演

## 关键技术决策

| 决策 | 理由 |
|---|---|
| 用 Electron BrowserWindow 而非 Playwright | catclaw 已用此架构验证稳定，迁移成本最低 |
| Worker thread 而非主线程 | 8 房并发解析事件可能 CPU 尖峰，避免阻塞 IPC |
| Keytar 而非 fs 加密 | macOS 原生 Keychain，安全等级最高 |
| JSONL 而非 SQLite 存原始事件 | append-only 性能 + 流式回放方便 + 体积可控（gz 压缩） |
| 不引入 Playwright | catclaw 没用，引入会增加额外依赖（约 200MB） |

## Self-Review

- ✅ 涵盖 spec §6 协议层全部需求
- ✅ catclaw 现有代码 1:1 迁移路径清晰
- ✅ 不为 Phase 5/6/7 做超前抽象（仅出共用基础设施）
