Skip to content

Releases: Mieksido/surfcore

SurfCore v0.7.9(修复 zip 安装自动更新 100% 失败)

Choose a tag to compare

@Mieksido Mieksido released this 30 Oct 00:54

修复:点「立即更新」反复失败,版本始终不变(verify.staging-mismatch)

这是开发端的结构性错误,影响所有 zip 安装(无 git)用户。

根因(两层):

  1. scripts/release-manifest.mjs 生成清单时扫的是开发工作目录,把 gitignore 的 _backups/(119 个)、audio/、obs-diag.json、本地改过的 docs 都列了进去;
  2. 用户下载的是 GitHub 归档,天然不可能包含这些文件,而 verifyStaging() 要求清单里每个文件都存在且哈希一致 → 恒假 → 每次都在应用前失败,永远到不了暂存。

修法:

  • 清单只列 git ls-files(归档里有什么就列什么);工作区有未提交改动时生成脚本直接报错;
  • 有版本号时下载 tag 归档(refs/tags/vX.Y.Z.zip),不再用会漂移的 main.zip;
  • 校验改为交集:只比包里真实存在的文件;缺失只计数记日志,不判失败;
  • 旧脏清单自愈:出现「新包没有的路径」就用包内清单覆盖安装记录(git 工作副本跳过);
  • 按提交判定有更新时用 releases/latest 的 302 补取版本号(显示与 tag 下载都需要);
  • 失败不再静默:state.lastApplyFailure 持久化,弹窗内常驻红块(code / 原因 / 建议 / 复制诊断 / 去下载新版);失败即删除本次 staging,不再累积磁盘。

⚠️ 升级方式(重要)

0.7.1–0.7.8 的 zip 用户无法自动升到本版——旧版仍会执行旧的“全量校验”,必然失败。请手动覆盖一次:

  1. 下载 https://github.com/Mieksido/surfcore/archive/refs/tags/v0.7.9.zip
  2. 解压后覆盖到安装目录(不要删除目录本身)
  3. 完全退出 DSH 再打开

从 0.7.9 起,清单与归档一致,zip 自动更新恢复正常。

验证

  • 新增 test-release-manifest:清单只含 git 跟踪文件、无运行数据目录、版本与 package.json 一致;
  • test-update-apply-result 新增:清单缺文件不判失败、tag 归档地址、失败清 staging、失败状态常驻;
  • npm run test:audit 85/85;插件测试全绿。

调查记录:docs/cases/2026-10-30-update-verify-staging-mismatch.md

SurfCore v0.7.8(语音页保存不再误报失败)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 16:27

修复:填完云端密钥点保存,红字「保存失败: toast is not a function」——其实已保存

根因:设置页里 toast 是浮层文案字符串,真正的通知函数叫 notify。h(VoiceTab, { toast }) 把字符串当函数传,保存成功后调用它抛 TypeError,又被 catch 改写成「保存失败」。语音页 8 处提示全部因此失效;真正的保存错误也会被这句顶掉。

修法:

  • 接线改为 toast: notify;浮层状态改名 toastText,彻底消除同名遮蔽;
  • 云端保存顺序改为「保存 → 刷新表单 → 通知」,通知失败不再改写保存结论;
  • 契约测试新增:所有 Tab 的 toast 必须是函数、VoiceTab 必须收 notify、状态必须叫 toastText。

文档:跨层契约规范

docs/guides/ERROR-REPORTING.md 新增一节(近期三起同类事故的总结):

  • 同名不同物必须改名(状态 toastText / 函数 notify);
  • 成功路径上的界面失败,不得改写业务结论;
  • 跨进程/跨层数据必须显式投影并读回断言(配置投影、启动快照)。

验证

npm run test:audit 84/84;插件测试全绿(含 test-client-contract 新断言)。

SurfCore v0.7.7(手动覆盖安装后不再提示旧版本)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 16:04

修复:手动覆盖到 v0.7.5 后,更新弹窗仍显示「v0.7.3 → 提交 db18896」

根因(两层):

  1. 手动覆盖只换文件,旧的 .installed-manifest.json / .installed-meta.json 还留在目录里(版本 0.7.3、旧提交号)。检查更新拿「旧提交 vs 远端提交」比,得出“有新提交”的假结论。
  2. updates.json 里缓存着上一次检查结果;手动升级后缓存没有失效,弹窗继续显示旧结论。

修法:

  • 检查时做安装证据版本校准:包里 release/install-manifest.json 的版本与现存清单不一致 → 刷新清单、重写只带版本号的安装记录(丢掉旧提交号),改走版本号比对;
  • 服务启动时丢弃已过期结果:安装目录 package.json 版本与缓存结果里的 installedVersion 不一致 → 丢掉该条,下次检查按当前版本重新算。

效果:手动覆盖到 0.7.5 的设备,弹窗会显示「v0.7.5 → v0.7.7」,而不是「v0.7.3 → 提交 db18896」。

验证

  • 新增两个用例:覆盖安装后证据刷新(清单/记录版本对齐、不再按旧提交判定)、过期结果被丢弃;
  • npm run test:audit 84/84;插件测试全绿。

说明

只改 surfcore;没有动语音包仓库。

SurfCore v0.7.6(语音工具常注册 + 设置不再被覆盖)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 15:51

修复:语音“三个闸门”与设置自己回弹

1. 工具常注册(不再“重启才生效”)

以前 qq_send_voice / qq_list_voices 是在 MCP 进程启动时按配置决定注不注册。改了开关没重启,模型手里根本没有这个工具,只能说“我没这个功能”。

现在工具始终注册;是否允许发送由桥接在调用时判定。

2. 拒绝时说清卡在哪一关

  • VOICE_DISABLED —— 语音总闸未开;
  • VOICE_TOOL_DISABLED —— 工具开关未开;

两者都带 hint 指到具体设置位置(如「设置 → SurfCore → 全部参数 → socialV2.voice.enabled」);MCP 保留 code/hint,模型能照着提示让用户去开。

3. 设置不再自己回弹

保存配置时回传加载时的版本戳(文件 mtime+size)。服务端发现配置已被控制台或其它窗口改过 → 返回 CONFIG_STALE 并拒绝整份回写,提示「重新读取后再改」,不再把别人刚改的语音开关覆盖回 false。

验证

  • 新增 test-voice-gates(21 项断言:版本戳语义、工具常注册、拒绝 code/hint、客户端带戳与 409 处理);
  • npm run test:audit 84/84;插件测试全绿。

说明

这一版只改 surfcore(插件/桥接/MCP),没有动语音包仓库。

SurfCore v0.7.5(更新提示不再反复弹同一提交)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 15:26

修复:点「立即更新」后又弹回来,提交号不变

现象:设备显示「SurfCore 本体 v0.7.2 → 提交 ddc0d81」,点更新后弹窗再次出现,仍是同一个提交。

根因:该安装目录既没有 .installed-manifest.json(上次安装清单)也没有 .installed-meta.json(安装记录)。v0.7.2 只在应用阶段做这个检查,失败后不记录 dismiss,所以下次检查又弹同一条;而弹窗不展示失败原因,看起来像“点了没反应”。

修法:

  • 检查阶段就判定:缺安装证据但有新版 → status=blocked(code=evidence-insufficient),带 blocked 字段,不再进入待办列表,弹窗不再重复;
  • 卡片常驻显示「更新被阻止:原因 + 建议」;
  • 自愈:目录里存在包自带的 release/install-manifest.json 时,自动补写 .installed-manifest.json 与 .installed-meta.json —— 手动覆盖安装过一次后即可恢复自动更新;
  • 远端版本号取不到时,用 releases/latest 的 302 tag 兜底,界面显示 v0.7.4 而不是提交号;
  • apply() 的 blocked 结果保留 code/hint,不再只给一句 error。

影响用户的操作

  1. 升级到 0.7.5(手动覆盖一次,因为当前目录缺安装证据,自动更新会被正确阻止);
  2. 覆盖后插件启动会自愈补登记;
  3. 之后再检查更新就能正常自动更新。

验证

  • 更新 fallback 用例 10 组(含 blocked、自愈、tag 兜底)全过;
  • audit 83/83;插件测试全绿。

SurfCore v0.7.4(管理员称呼投影修复)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 15:07

修复:设置了称呼「弥叭」,QQ 里仍叫「管理员」

现象:v0.7.2 用户把管理员称呼设为「弥叭」,QQ 提示词仍生成 称呼「管理员」,模型输出「弥叭管理员」。

根因:

  • 保存管理员卡片时,projectAdminSave 只把 ownerQQ 投影到 QQ 的 config.json,displayName/biliName/gentle 留在 unified;
  • 桥接是独立进程,只读 QQ config.json,读不到 unified 里的称呼;
  • 没名字时 adminDisplayName 兜底返回 '管理员',把身份词当成了称呼;
  • 桥接只在启动时读配置。

修法:

  • 完整 admin 投影写入 QQ config(displayName/biliName/gentle);
  • adminDisplayName 没名字时返回空串,不再把「管理员」当称呼;提示词明确「管理员是身份/权限标记,不是称呼」;
  • 插件启动时 ensureAdminProjection() 自动补投影;桥接在跑会自动重启;
  • adminFromSources 在 unified 未写 admin 时回退 QQ 已有投影,避免升级丢称呼。

存量用户:升级后无需手动重存;插件启动会补投影并重启桥接,QQ 提示词变为 称呼「弥叭」。

验证

  • test-admin-identity、test-admin-prompt-wiring 通过;
  • npm run test:audit 83/83;
  • 本机 config 实测提示词含 称呼「弥叭」、不含 称呼「管理员」。

调查记录:docs/cases/2026-10-29-admin-display-name-projection.md

SurfCore v0.7.3(中文目录选择乱码修复)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 14:47

修复:原生「浏览…」选中文目录后路径乱码

  • 现象:设置页「导入离线包」选中文目录(如 D:\...\中文oice-local),弹窗报 IMPORT_PATH_NOT_FOUND,路径里出现 �/◆;实际目录存在。
  • 根因:Windows PowerShell 5.1 重定向 stdout 使用 GBK,Node 按 UTF-8 解码,中文路径损坏。
  • 修法:PowerShell 把选中路径写进 UTF-8 临时文件,Node 读文件后删除,不再解析 stdout;导入 / 安装目录 / SnowLuma 三处「浏览…」共用 handleBrowse,一处修复全部生效。

调查记录:docs/cases/2026-10-29-browse-chinese-path.md

验证

  • node --check plugins/surfcore/lib/index.js 通过;
  • PowerShell 中文路径写 UTF-8 临时文件、Node 读回一致(已实测);
  • 真实选择框需重启 DSH 后手点一次「导入离线包」选中文目录。

说明

这一版基于 v0.7.2;未改动更新检查/备份/回滚逻辑。

SurfCore v0.7.2(可选 GitHub 令牌,解决限流)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 14:18

新增:可选 GitHub 令牌

更新卡片下新增「GitHub 令牌(可选)」:

  • 粘贴一个只读 token 后,检查更新走你账号的 5000 次/小时,不再受匿名 60 次/小时(按 IP 共享)限制;
  • 留空仍按匿名请求,行为不变;
  • 令牌只存在本机 config.json 的 updates.githubToken,界面不回显、日志不打印、诊断信息不含它,可随时「清除」。

生成方式:GitHub → Settings → Developer settings → Fine-grained tokens → 只需要 Public repositories (read-only)。

文案

  • 未配置时明确提示「匿名 60 次/小时,共享 IP 时可能遇到“查不到:限流”」;
  • 配置后显示「已配置(走账号额度 5000 次/小时)」。

验证

  • token 注入/不注入两态断言通过;
  • audit 83/83;插件测试全绿。

说明

这一版只是把“限额”从 60 提到 5000;GitHub 的匿名 60 次限制本身无法在设置里取消(平台固定规则)。

SurfCore v0.7.1(更新系统不再静默失败)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 14:04

修复:更新系统不再把失败伪装成成功

一、检查更新必须说清三种结果

  • 没有 git、没有安装记录时,改为按版本号比对,不再直接判「最新」
    (事故现场:本地 0.6.22、远端 0.7.0,却显示「最新 v0.6.22→1ad2042」且没有更新按钮);
  • 取不到远端版本号 / 远端不可达 / rev-list 失败 → 显示「查不到:原因(建议)」,不再冒充「最新」;
  • 卡片按状态渲染:有更新 / 已是最新 / 查不到 / 被阻止 / 检查失败。

二、失败必须给出原因与下一步

  • 下载:download.http-XXX / download.timeout / download.network / download.write-failed;
  • 解压:verify.unzip-failed / verify.bad-archive;
  • 更新助手:stage.helper-missing / stage.spawn-failed(不再「下载成功即成功」);
  • 「已下载待重启」单列 pending_restart,明确写「尚未更新」;
  • 重启后卡片常驻「上次更新」:已校验成功 / 已应用未验证 / 待重启 / 失败原因 + 建议;
  • 新增「复制诊断」。

三、不弄丢用户的东西

  • 覆盖前读包内 release/install-manifest.json,先校验包与清单一致;
  • 本地改过的文件 → 阻止自动更新并列出;既无清单也无安装记录 → 阻止并给手动下载指引;
  • 覆盖前真备份(逐文件回读 sha256),备份失败不覆盖;
  • robocopy /MIR 改为 /E:只覆盖、不删除,用户自建文件永不误删;
  • 更新后校验目标版本,不符写 .failed(verify.version-mismatch);
  • 新增回滚:从备份逐文件恢复并校验哈希与版本,失败保留备份并给人工恢复指引。

工具

  • 新增 scripts/release-manifest.mjs:发版前生成 release/install-manifest.json(当前 605 文件)。

验证

test-update-apply-result 40 项断言、test-update-version-fallback 7 组、audit 83/83、插件测试全绿。

已知残余

下载包的 sha256 / 数字签名尚未做(当前校验的是「包内文件与清单一致」);签名硬化列入后续版本。

SurfCore v0.7.0(离线导入 + 身份审计 + 云端凭据安全 + 失败可见)

Choose a tag to compare

@Mieksido Mieksido released this 29 Oct 12:47

新增

语音包离线导入(本地/云端)

  • 设置页新增「导入离线包…」:选解压出来的 voice-local / voice-cloud 文件夹即可,先展示类型/来源/目标/大小/版本/完整性确认;
  • 默认复制源目录、不移动;先校验类型/必需文件/manifest 哈希/目标运行状态/磁盘空间,再写目标;旧包先备份,失败自动恢复;
  • 导入后改写 .venv/.venv-gsv 的 pyvenv.cfg 指向包内 runtime;没有 config.toml 时从示例生成并清空云端凭据;
  • 失败有 code / error / hint / missing 弹窗,可重选目录、先停止语音包;支持进度/取消、删除源目录/旧备份。

安全

  • 云端语音包不再携带任何预置密钥:安装/导入时清空 cloud_app_id / cloud_access_key,用户自行填写;未配置时启动直接给出可照做的提示;
  • 回答者身份由程序标注:主人 / 群友(非主人);[仅主人] 只收窄回答权,非主人不消费提问、不吞消息;
  • 主人特权操作审计:7 类事件写入 state/owner-audit.jsonl,1MB×5 轮转 + 90 天清理,设置页可查,agent token 403。

修复

  • SnowLuma 装在自定义目录时检测不到:增加手选路径「浏览…」,显式路径只验证它本身;检测失败弹错误报告(列出试过的位置 + 下一步);
  • 参考音频「下载」在 Electron 里点击无反应:改成 fetch→Blob 下载,并加「打开所在目录」;
  • 语音安装进度文案去重(阶段名已带百分比时不再拼日志尾)。

文档

  • 新增 docs/guides/PERMISSIONS.md、docs/guides/ERROR-REPORTING.md、docs/guides/12-LESSONS-ARCHITECTURE.md、docs/RELEASE.md、docs/RELEASE-QUEUE.md;
  • 案例 6 篇(离线导入、参考音频下载、云端凭据、SnowLuma 检测、提问身份审计等)。

重要说明:离线语音包本版不发布

  • 本地离线包(4.7G)内含 age12 微调权重,其素材链为「微软 Edge 晓伊 → CosyVoice 克隆 → 参考音频 → 训练」,上游授权不可考,不得公开分发;
  • 云端离线包(249M)内含 piper(GPL-3.0)且为同进程组合,组合定性未决;
  • 两个 zip 只留在本地 POC 目录;导入功能已在 SurfCore 内,拿到合规包后可自行导入。

验证

npm run test:audit 81/81;npm run test:regressions 全过;插件测试全绿;真实 7.9G 导入跑通(源目录保留、pyvenv 指向最终 runtime)。