feat(platform/P1): 配对码跨设备 + 认领打磨 + 键盘语音双套资产(两 agent 并行 + 集成收口)

配对码与认领(B):
- POST /api/pair/start|join:6 位码 10 分钟单次有效、仅存内存零泄露面、5 连错 per-IP 封锁 10 分钟;join=新设备档案按桶 LWW 并入老档案+token 改绑(复用 claim-merge);日志脱敏
- 大厅:生成配对码(speechSynthesis 逐位朗读+aria-live+重听)/从其他设备转入(one-time-code 输入框)/从本机移除/401 过期自愈引导/aria 补全/玩家名可听化
- api-smoke 12/12(配对全流程/频控/封锁/过期自愈/移除/digitsToCn)+ CDP 真浏览器 E2E 22/22

键盘语音双套资产(A,路线 a):
- 164 变体句(8 游戏 grep 实测,超出调研预估 153:含轻触/点两下/按住变体)×2 性别=328 生成 656 文件 36.9 分钟;触屏主资产零字节改动(PCM 逐样本比对后还原)
- 引擎 _candidates 三跳路由 key→gender→平铺(inputMode 跟随);缓存键 mode|gender|id;engine-input 新增 K9 路由断言(72 条)
- check_all_assets 增 key 列(两性别一致且 ⊆ 主集)8 游戏全绿;voice_text.json 增 __key__ 表;live control.mjs key 会话对照键盘措辞验收(集成)
- voice-ids 文档:8 游戏键盘变体小节由 KEY_LINES 程序化生成(与台词表逐字断言一致);补齐 storyline/escape/simon 三个历史缺失文档;design README 增 new8 索引
- mock 适配:playtest persona 加 maxTouchPoints=1 保持触屏链断言语义零改动

回归:12 套件全绿(8 playtest + guide 122 + engine-voice 19 + engine-input 72 + api-smoke 12/12)
同批入库:批次 2/3 已完稿设计文档(17-abyss/18-detective/19-cipher 全套,20-22 在途)
This commit is contained in:
xsl
2026-09-10 00:43:23 +08:00
parent ede522dda1
commit 566241e05d
694 changed files with 4242 additions and 168 deletions
+39 -2
View File
@@ -7,6 +7,10 @@ P0-B 起采用**无感档案系统**(设计见 `research/platform/01-identity.
认领(命名 + 设 PIN)完全可选,同设备多玩家靠大厅档案切换器隔离,进度按游戏分桶、
服务器时钟裁决的桶级 LWW 同步。8 个游戏目录零改动——分桶/倒灌发生在大厅层。
P1 增补**配对码跨设备**(`01 §4.2` 旅程 3):老设备在「档案管理 → 转移到新设备」
生成 6 位配对码(逐位朗读),新设备输入即可把本机进度按桶 LWW 合并进老档案;
token 180 天滑动过期,到期后大厅引导「重新登录或新建」。
## 启动
```bash
@@ -30,6 +34,9 @@ PORT=8001 SG_DATA_DIR=/tmp/sg-data tools/node/bin/node web/server/server.js
- PIN 以 **scrypt 哈希**(N=16384, r=8, node:crypto 内置)存储,不再明文;
- token **180 天滑动过期**、可 logout 吊销、惰性清扫,不再无限增长;
- 匿名建档端点有 per-IP 频控(默认 10 个/天,`SG_DEVICE_LIMIT` 覆盖);
- 配对码:**仅存内存不落盘、不进日志**;10 分钟单次有效、同档案同时仅 1 个有效码、
`pair/start` per-IP 频控(默认 20 个/天,`SG_PAIR_LIMIT` 覆盖)、`pair/join`
**连错 5 次封锁同 IP 10 分钟**(10^6 码空间 × 5 次 → 单窗口猜中概率 ≤ 5×10⁻⁶);
- 仍是 HTTP 明文传输、无 TLS、无登录失败退避——威胁模型是「家庭 WiFi 可信 +
防家人恶作剧」。公网开放 API 的前置清单见 `docs/deploy.md`「以后若要开放用户系统」。
@@ -81,8 +88,9 @@ PORT=8001 SG_DATA_DIR=/tmp/sg-data tools/node/bin/node web/server/server.js
### POST /api/login —— 旧客户端兼容
- 请求:`{ "username": "uuu", "pin": "0858" }`
- 成功:`200 { "ok": true, "token": "…", "username": "uuu", "profileId": "uuu", "name": "uuu" }`
- 成功:`200 { "ok": true, "token": "…", "username": "uuu", "profileId": "uuu", "name": "uuu", "claimed": true }`
- v1 用户迁移后照常可登录;若存量 PIN 尚为明文,验证成功时**透明升级**为 scrypt 哈希。
大厅在 token 180 天过期后的「名字 + PIN 重新登录」路径也走本端点(无需有效 token)。
### POST /api/register —— 旧客户端兼容(建议改用 device + claim)
@@ -93,6 +101,33 @@ PORT=8001 SG_DATA_DIR=/tmp/sg-data tools/node/bin/node web/server/server.js
- 请求头:Bearer;成功:`200 { "ok": true }`
### POST /api/pair/start —— 生成配对码(老设备出示,P1)
- 请求头:Bearer(匿名 / 已认领档案均可)
- 请求:`{}`
- 成功:`200 { "ok": true, "code": "483920", "expiresAt": "2026-09-09T12:10:00Z" }`
- `code`:6 位数字,**10 分钟有效、单次使用**;同档案同时仅 1 个有效码,
重新生成会立即使旧码作废(单码作废 + 重生成即调用本端点)
- 码仅存服务器内存:进程重启即全部失效(老设备再点一次「生成配对码」即可)
- 失败:`401` / `429`(同 IP 当日生成数超 `SG_PAIR_LIMIT`,默认 20)
### POST /api/pair/join —— 配对码转入(新设备,P1)
- 请求:`{ "code": "483920" }`;请求头 Bearer 可选(新设备当前匿名档案 token)。
token 无效/过期时按「无档案设备」处理,不报 401。
- 成功(带 token,与 claim-merge 同语义——**新设备当前档案按桶 LWW 并入码对应的
老档案**,方向裁定:本地新进度并入老档案):
`200 { "ok": true, "merged": true, "mergedGames": 2, "profileId": "p_…", "name": "玩家一", "token": "<原 token,已改绑>", "claimed": false }`
- 成功(无 token,全新浏览器):直接为老档案签发新 token(`merged: false`)
- 成功(joiner 已是该档案):幂等返回当前 token(`merged: false`),码照常作废
- 成功即**作废该码**(单次使用),并发重复 join 靠同步删除保证原子
- 失败:
- `400`(码格式非法,不计入连错)
- `404`(码不存在 / 已过期 / 已用过——统一文案不泄露状态;**计入连错**)
- `409`(joiner 当前档案已设 PIN:请先「+ 新玩家」建匿名档案再转入;不消费码)
- `429`(同 IP 连错 5 次 → 封锁 10 分钟)
- 日志脱敏:配对码明文不进日志(与 PIN 同待遇)
### GET /api/progress —— 读进度
- 单桶(旧形状,游戏兼容,如 radio 直接读 `data`):
@@ -153,4 +188,6 @@ tools/node/bin/node web/server/test/api-smoke.mjs # API 冒烟 + 大厅分桶
```
覆盖:device 建档/重附着、进度 PUT-GET-LWW 冲突(含 legacy 放行)、claim 命名/设 PIN/
合并、旧用户登录迁移、per-IP 频控、/server/ 静态封禁、localStorage 分桶逻辑(H1-H4)。
合并、旧用户登录迁移、per-IP 频控、/server/ 静态封禁、localStorage 分桶逻辑(H1-H6)、
配对码全流程(I1:start 规约/join 桶级 LWW 合并/单次作废/无 token 转入/幂等/日志脱敏)、
配对码防护(I2:join 5 连错封锁、start per-IP 频控)、token 过期 401 路径(J1)。