dsh-chubby-whale 1.1.57 → 1.1.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,68 @@
4
4
 
5
5
  ## [未发布]
6
6
 
7
+ ## [1.1.59] - 2026-11-07
8
+
9
+ ### 修复(B82 装配层分身:云端 TTS 从未接线)
10
+ - **「云端 → edge-tts → 浏览器」实际只有两层**:`cloudTts` 在 `index.ts` 构造出来却从未注入(`git log -S createCloudTts` 自 1.1.37 起没有一次 `cloudTts:` 实参),而路由一直依赖 `opts.cloudTts`。配了 `cloudVoice.apiKey` 的主人也一直走 edge-tts
11
+ - **TTS 常驻 python 子进程无人回收**:引擎在 `routes/tts.ts` 工厂内部自建,`dispose()` 传不出来,只能等 5 分钟 idle;改由装配层建引擎并登记 `ctx.effect`
12
+ - **`routes.ts` 的 `HandlerSet` 是 `any`**:表里写错 handler 名只有运行期才炸;改成各工厂 `ReturnType` 的编译期契约
13
+ - 死导入(`deepFreeze`、`mkdirSync/existsSync/copyFileSync/cpSync`)、死变量 `cloudTts`、两份重复的 Key 读取器、poem 的不可达 `dataDir` 注入一并清掉
14
+
15
+ ### 修复(B83 ASR 并发治理)
16
+ - **ASR 没有单飞**:`onWindowDone` 可重入,两段识别并发(实测第二个 `/asr` 照发、`uploaded` 计 2)→ 现在忙则丢(不排队:识别是「说完马上回话」的交互,排队会让第二句等第一段,且断网时队列无界)
17
+ - **ASR 没有纪元**:Stop→start 后旧结果当新话(实测旧话被 `onHeard` 送出);现在 `start`/`stop`/`stopSpeaking` 各 +1,`prepare` 之后、`res.json()` 之后、catch 里都校验,过期一律不送、不写缓存、不改状态
18
+ - **P0 打断不作废在途识别**:`stopSpeaking` 只处理 TTS;现在同时 abort 在途 ASR(`abortAsrFlight`)
19
+ - **主动作废被记成识别失败**:abort go catch 后无条件 `asrFail++`;现在过期/主动取消不计失败
20
+
21
+ ### 修复(B84 组件入口:叙述锁与文案)
22
+ - **诗尾 onDone 会解掉说书的锁**:念诗播到一半点「讲段书」,诗 segment 播完的 `clearNarrate()` 把说书刚上的锁清掉(`narration.ts` 只有一份 `until`)→ 说书段没有锁、气泡不更新、麦克风提前拾音。现在锁按**持有者令牌**归属:`clearNarrate` 只在仍是自己持有时才清;「停止说话」与过期看门狗走权威清锁
23
+ - **说书长段没有朗读兜底**:`story.ts` 只在估算 ≤2 分钟时挂兜底,1000 字≈4 分钟,`onDone` 一丢就永久「正讲着呢」。入口在 narrate 后挂**只报警不放锁**的探针(`max(30s, 1.5×估算)`),到点写 `__chubbyWhale.narrateOverdue` + `narrateOverdueInfo`
24
+ - **文案与实现相反**:「语音待命:开(先喊「大肥鱼」,本鲸才答话)」「待唤醒(先喊…)」——唤醒词自 2026-11-06 起已不是拦话门槛;改为「随时可以说,喊不喊名字都行」等真实表述
25
+ - `wakeHint` 不再补「重开语音待命后生效」:`syncWakeWords` 已让换词即时生效,那句提示本身是错误信息(`test-actions-rest` 对应 2 条期望同步更新)
26
+
27
+ ### 修复(B85 基础设施)
28
+ - **读图无等待上限**:`FileReader` 在安全软件拦截/文件被占用时会永不回调,`handleFileDrop` 永远 pending、气泡卡在「本鲸看看这张图…」;加 30s 超时 + `abort()` + 收尾清定时器
29
+ - **检查器自检探针**:新增 `test-boundaries-guard.mjs`,临时塞入「路由直连 store/cache」两个违规探针,断言 `check-boundaries` 必须 exit 1 并报出 B6,删探针后回到 exit 0(防「规则假绿」重演)
30
+ - **`tools/test-count.mjs`**:逐脚本取最后一条汇总行求和,避免人工加总漏掉「全部通过:N/N」这类格式(此前手算虽正确,但工具化后可复跑)
31
+
32
+ ### 测试
33
+ - 新增 `test-assembly.mjs`(20)、`test-asr-concurrency.mjs`(49)、`test-entry-narration.mjs`(37)、`test-image-reader.mjs`(7)、`test-boundaries-guard.mjs`(9),均接入 `npm test`
34
+ - 全链 **2845 项 0 失败**(`node tools/test-count.mjs` 实跑:39 个套件,失败 0,未识别 0)
35
+
36
+ ## [1.1.58] - 2026-11-07
37
+
38
+ ### 修复(B74 配置/备份/迁移分身)
39
+ - **坏 `config.json` 被静默吞掉**:用户只看到「Key 没了、语音没反应」;现在按「未配置」降级并**只告警一次**(坑 35:没反应比报错更糟)
40
+ - **`sanitizePluginConfig` 可被原型链注入**:`{"__proto__":{...}}` 会改结果对象原型(实测 `out.dataSource` 能从原型读到);已拒收 `__proto__`/`constructor`/`prototype`
41
+ - **`prefs.json` 是数组时缓存开关永远写不进去**:`load` 原样返回数组/标量,属性写入被 `JSON.stringify` 丢弃;已归一 + 单次提示
42
+ - **迁移半途失败会留下「假已迁移」**:直写目标,`copyFileSync` 失败留半截文件,下次 `existsSync` 为真就永远跳过;改为「临时名 + link 优先/rename 兜底」原子落地
43
+ - **自动备份快照非原子写 + 时间口径双源**:崩溃会留半截 JSON 被当成一份备份;裁剪按文件名、`lastAt` 按 mtime,目录被拷贝后时间漂移。已统一(文件名时间优先、mtime 兜底)+ 原子写 + 定时器按运行目录去重
44
+
45
+ ### 修复(B75 主代理:备份定时器停不下来)
46
+ - **`ctx.on('dispose')` 在 cordis 里永不触发**:卸载只派发 `internal/plugin`,全仓没有 dispose 事件——热重载/禁用后旧备份定时器会一直写到进程退出。改用 `ctx.effect` 把「停备份」登记成 fiber 清理器
47
+
48
+ ### 修复(B76 媒体/提醒分身)
49
+ - **提醒轮询收尾不彻底**:`start()` 只返回 `stopPoll`,全仓没有 `dispose()` 调用 → 卸载后待播队列与 gap/retry 定时器继续冒泡/朗读(基线实测还播了第 2 条);收尾改为撤定时器→清队列→停轮询
50
+ - **收尾后迟到的轮询响应仍入队**:补 `alive` 守卫(并发纪律「每个 await 之后校验我还是当前那次」)
51
+ - **空正文提醒被静默丢掉**:`if (it && it.text)` 让「时间到了」兜底成死代码;现在空正文走兜底
52
+ - **顺延跨零点提示错误**:23:55 顺延到 00:05 只说 `00:05`,会被读成今晨;现在显示「明天 00:05」,多天显示日期
53
+ - **`image.ts` 抛错漏 revoke**:`createObjectURL` 之后 `new Image()`/赋值抛错时只 `resolve(null)`,泄漏一份 Blob;已覆盖
54
+ - **TTS 抛错把「看图成功」改判成失败**:朗读回调裸在 try 里;已包 try/catch
55
+
56
+ ### 修复(B77 语音桥/唤醒分身)
57
+ - **打断词漏「别」「不」**:桥里的整句正则只有「停/停下/…/够了」,而宿主专门放行五个打断词、队列测试也断言「别」「不」必须放行 → 朗读中说「别」「不」被当正文送模型,停不下来;已统一走 `shared/noise.ts` 的 `INTERRUPT_WORDS`(并认「大肥鱼,停」),叙述锁分支同口径
58
+ - **`beforeunload` 匿名监听每次重建都多挂一个**:dispose 摘不掉(旧监听攥着废弃实例);改为单例 + dispose 摘除
59
+ - **`wakeInfo` 交出内部词表数组**:调用方可直接改唤醒门词表;改为返回副本
60
+ - **自定义唤醒词后旧词谐音仍生效**:换词后仍能被「大肥鱼/大粉鱼」叫醒并剥正文,与「只用这一个词」的契约冲突
61
+ - **单字唤醒词被误剥**:「好的」会剥成「的」送模型;单字只认整句
62
+ - **浏览器朗读兜底首句被吞**:上层每次兜底前都 `cancel()`,而模块只在 `busy` 时让 80ms → 立刻 speak 被吞;改为 cancel 后统一隔 80ms
63
+ - **看门狗判死不开扬声器回收**:只 resolve 不 cancel,下一段(可能是 edge-tts 音频)会叠着播;现在判死即 cancel
64
+
65
+ ### 测试
66
+ - 新增 `scripts/test-prefs-config.mjs`(84)、`test-media-reminders.mjs`(171)、`test-voice-bridge.mjs`(59),均接入 `npm test`
67
+ - 全链 **2723 项 0 失败**(2409 + 84 + 171 + 59)
68
+
7
69
  ## [1.1.57] - 2026-11-07
8
70
 
9
71
  ### 修复(B73 主代理:统计与备份)