Repository navigation
Releases: Fish-under-sea/FISH-Dock
Release list
FISH Dock 2.1.1
FISH Dock 2.1.1
定稿:2026-11-05。宿主 2.1.1,兼容插件 2.x(官方目录当前 2.2.0:Clash / Magpie / 飞书)。
补丁版,两件事:第三方插件识别排除官方三个插件(#72),以及把自动检查更新里的 Gitee 拉取恢复。
一、第三方插件排除官方三个(#72)
现象:官方三个插件在 npm 上也有合规包名——@fish-under-sea/fish-dock-plugin-feishu、…-clash、…-magpie(plugins/tools/build-npm-packages.mjs 发布的名字)。第三方页的默认发现词就是 fish-dock-plugin,npm 检索因此把这三个官方包排在最前面,当作「可安装的第三方插件」列出来。它们不是第三方:身份由官方目录授予(Source::OfficialCatalog + 主仓 Star),从第三方通道装进去会被登记成第三方身份,装完也和商店条目对不上。
改动(只动 Dock,插件仓 catalog/stable.json 一字未动):
- 官方身份只剩一份定义:
plugin_store::OFFICIAL_IDS(fish.feishu/fish.clash/fish.magpie)。目录校验与第三方页排除都读它,不再各抄一份数组;官方 npm 包名也仍由plugin_store::npm_package构造。 - 判据只认官方那份包名:
plugin_thirdparty::official_package比对@fish-under-sea/fish-dock-plugin-<slug>,大小写无关。无 scope 的fish-dock-plugin-magpie、@acme/fish-dock-plugin-magpie都不是官方包,照常按第三方处理——依据是「官方身份」,不是「名字里带 magpie」。 - 检索两路都排除:精确包名元数据查(①)与
/-/v1/search(②)命中官方包时不展示,并计入新字段official;hidden仍是「不符合fish-dock-plugin-*命名」的口径,两者不混在一个数字里。命令返回体search_json与前端夹具app/test/fixtures/thirdparty-search.json同步新增official。 - 安装口零网络拒绝:
resolve_via在命名判据之后、任何请求之前拦下官方包,返回明确理由(…` 是官方插件:请到「官方插件」页安装或更新),不再出现「装了但起不来」或「装成第三方身份」。 - 界面说出来,不静默消失:第三方页提示「本页另有 N 个官方插件的 npm 包:官方插件请到「官方插件」页安装或更新」,规则行也补一句「官方插件不在本页」。
二、自动检查更新恢复 Gitee 拉取
背景:2.1.0 时 Gitee 通道是「配了才启用、默认空」,理由是当时主仓 Gitee 只有 tags、没有任何发行附件。现在那边已经有发行资产,实测(2026-11-05,匿名 API):
api/v5/repos/Fish-under-sea/FISH-Dock/releases返回v2.1.0,附件FISH-Dock-Update-2.1.0.exe与FISH-Dock-Update-2.1.0.exe.sha256;- 那份
.sha256是5513ee3a5605a67e256fc23ecb68625ae92a0c0c18fe1f1a18e97b9fadd84406 FISH-Dock-Update-2.1.0.exe,与 GitHub v2.1.0 的assets[].digest(sha256:5513ee…4406)逐字相同。
改动:
config::GITEE_REPO_DEFAULT = https://gitee.com/Fish-under-sea/FISH-Dock,url.gitee_repo改为默认启用;off/none/0(大小写不敏感)是明确的关闭写法——空串在这套 getter 里的既定语义是「没覆盖,用默认」,不能兼作开关。- 设置页「自动检查更新 · 从哪里下载」不再显示「Gitee 未配置」,
自动 / Gitee / GitHub三个选项都在(giteeConfigured由gitee_repo()是否为空决定,不变)。 - 探针名错位修复(
AGENTS.md§8.1 记的上游缺陷①):检查器一直按wakuwaku-v<ver>-windows-x64.exe(便携 exe 的历史名)去发行里找资产与<exe>.sha256,而 2.0.x 起的发行只有FISH-Dock-Setup-<ver>.exe/FISH-Dock-Update-<ver>.exe——所以 Gitee 侧连.sha256都探不到,整条通道等于没接。新增updates::update_asset_name(FISH-Dock-Update-<ver>.exe,即应用内更新真正先下载的那份),Gitee 探针改按它取摘要。 - 两侧比的是同一个文件:
github_release的digest也改取安装器载荷(Update 包)的摘要——它是 Gitee 侧唯一放上去的那份,两边摘要因此可以直接比对;两边不一致时仍按 2.0.4 的口径判conflict、不装。发行里没有安装器载荷(例如只有便携 exe 的历史版本)时摘要为None,不拿便携 exe 的摘要去比,避免造出假冲突。 Found.exe(便携载荷)语义没变:Gitee 不放便携 exe,所以恒为None;不拿安装器顶替「换掉自己的 exe」。- 只认完整的 Gitee 发行:探不到
FISH-Dock-Update-<ver>.exe.sha256就返回「那一侧没有」,不用半份发行(只传了 Setup 的那种)宣布有新版本,也不制造跨来源摘要冲突。Gitee 发布仍需「Update exe + 它的.sha256」两件齐全。 - 放行范围:
allowed()仍是「前缀 + 边界字符」;默认gitee_repo非空只意味着同一个 repo 的 Gitee 资产可下载,插件仓(FISH-DOCK-Plugins)与别人的仓照旧拒绝。
三、版本位
app/src-tauri/tauri.conf.json与app/src-tauri/Cargo.toml:2.1.1(同版)。app/integrations/claude-code/.claude-plugin/plugin.json:2.1.1。它此前停在 2.0.4,而one_version_everywhere要求三处一致——2.1.0 那版漏了这一处,本版把这条测试修回绿。AGENTS.md§八 与app/docs/config-keys.md同步(Gitee 现状从「有 tags 无附件」改为「有 v2.1.0 发行资产、默认启用」)。- README 的版本卡与发行段由
tools/render-readme.mjs从tauri.conf.json渲染,出包时统一重渲染,不手改 PNG。
四、测试
plugin_thirdparty::tests::the_three_official_plugins_are_never_third_party(新增):判据只认官方包名、大小写无关;发现词检索把官方三个全部排除且official = 3、hidden只数真正命名不合规的包;安装口命中官方包时asked == 0(零网络)且理由指回官方商店。plugin_api::tests::the_search_command_carries_every_field_the_ui_reads:命令返回体与前端夹具字段一一对应,新增official。config::tests::mirror_prefixes_default_to_empty_and_prefer_whitelists_or_falls_back:gitee_repo()默认等于GITEE_REPO_DEFAULT;off/ none / 0关掉;空白=没覆盖。sources::tests:the_gitee_attachment_candidate_is_on_by_default_and_off_switches_it_away、a_malformed_gitee_repo_yields_no_candidate、default_candidates_are_locked_with_the_gitee_channel_off按新默认重钉。updates::tests:the_gitee_endpoint_is_on_by_default_and_switches_off_explicitly、the_gitee_candidate_stays_inside_the_same_allowance、the_allowance_stops_at_the_repository_boundary、github_read(摘要取安装器载荷、便携资产不掺和)。- 前端(
node --test --test-isolation=none app/test/extension-panel.test.js,75 passed):被排除的官方插件数量单独提示,并指回官方插件页、官方插件与命名不合规同时出现时,两句提示都在。
五、安装包
| 安装包 | 使用场景 |
|---|---|
FISH-Dock-Setup-2.1.1.exe |
首次安装、基础资源缺失或修复安装,包含完整离线运行时。 |
FISH-Dock-Update-2.1.1.exe |
已有 2.1.0 且基础资源完整时的精简更新,只含相对 2.1.0 的变更;缺失或损坏时提示改用完整包。 |
精简包只对 2.1.0 有效(差分包按基线生成,跨版本会缺文件)。
六、已知限制
- 便携版仍无应用内更新载荷:发行不提供便携 exe,
Found.exe保持None;应用内更新只对安装版生效(装的是发行里的安装器)。 - Gitee 侧必须传 Update 包:只传
FISH-Dock-Setup-*.exe时 Gitee 不参与「有新版本」的判断(见第二节「只认完整的 Gitee 发行」)。app/scripts/gitee-release.ps1默认只传 Setup,要恢复完整的国内更新链,发布时需再跑一次并传 Update 包和它的.sha256。 - 官方插件仍只以
catalog/stable.json+ npm 双源 + GitHub Release 为来源;本次改动没有把 Gitee 接进插件三源。
FISH Dock 2.1.0(插件下载回退 / 卡面与滚动锁 / 自启可观测性 / 国内镜像)
FISH Dock 2.1.0
定稿:2026-11-04。宿主 2.1.0,兼容插件 2.x(官方目录当前 2.2.0:Clash / Magpie / 飞书)。
本版收口主仓六个 OPEN issue 里的四条 + 一条历史欠账,并补齐 2.0.4 的两处遗留。
一、插件下载:三源全灭时按来源回退上一可用版本(#68)
目录里的版本跑在制品前面时(目录写了新版、npm 与 Release 还没有),用户过去只会看到「检查网络或代理后重试」——而网络是好的。
- 版本候选:优先读官方目录的版本历史条目;目录没有该字段时用编译期内置表
KNOWN_GOOD(12 条,摘要取自官方发行,经 GitHub Releases API 的assets[].digest逐条核验 12/12 一致)。比目录更新的内置条目不作回退目标。 - 下载前探活:对每个来源每个候选做
HEAD,只对 404/410 定论「制品不存在」;403 不定论,照旧发起完整下载(限流或 CDN 拒 HEAD 证明不了缺制品,一次限流不该把可用来源判死)。 - 按来源降级:
fetch_from改为「逐来源 × 逐候选(新→旧)」,某条来源最近可用版本成功即返回,并带出实际装上的effective_version;三源都失败时把各候选的 attempts 一并交给面板。 - 界面保留新版:目录数据一个字节不改,卡片照常显示最新版;装完后提示「已安装 vX;目录最新 vY 暂不可用」,走绿色成功态而不是红色失败。
- 摘要零放宽:期望摘要只取候选自带的官方目录摘要,装上的 digest 绝不等于自算摘要;错摘要按该来源失败处理并继续下一候选。
- 跳转判据修正:
hop()从「只认两个 GitHub 资产域」放宽到「官方发行资产域 或 与当前请求同站点域」。这修掉了一个真实缺陷——npmmirror 的 tarball 的HEAD与GET都 302 到cdn.npmmirror.com,此前一律被拒,于是「三源」实际只有两源可用,而国内用户最需要的恰好是死掉的那条。仍要求 HTTPS、拒绝跨域与端口、拒绝后缀伪装(npmmirror.com.evil.example照样拒)。 - 可诊断性:结构化下载失败按内容语义记进
recent.log(恰好一条,不重复),探活判死补 stderr;失败面板逐源显示 来源 · 结论 · HTTP 状态码 · 失败阶段 · 可复制 URL,title保留原始技术串。
二、插件卡与浮层(#69)
- 「更多」展开区从约 210px 压到 105px(目标 ≤120),日志↔卸载 9px(目标 ≤40);菜单行 32px 整行可点、组内仅一条分隔线、卸载置组末且常态红。
- 目录名与功能页入口合并成一行(12px 次级色 + 省略 + 点击复制)。
- 已安装卡标题 17px/600,状态点 6px、状态↔版本 8px,行内最大留白 10px。
- Dock 弹窗锁滚动:确认弹窗打开时底层列表不再被滚走;进度浮层与失败块在锁定期仍可滚动(
UNLOCKED_SCROLLERS具名清单),关闭/取消/Escape/点背景四路都解锁并还原scrollTop。 - 新增「插件卡与浮层视觉规范」注释块(字号/行高/间距/危险色/滚动锁口径),防 #63 式反复。
三、自启失败可定位(#70)
预算与重试调度在 2.0.4 已存在(冷态一次尝试共用 60s、整轮 180s、每插件最多 4 次尝试),本版只补可观测性与对称性:
Failure增phase(verify/spawn/hello/activate/service)、attempt、elapsedMs(增字段,旧plugin-failures.log仍可解析)。log_finish的 ready/stopped 也落盘(复用单一写入路径,failed 不重复)。- 手动启用首次失败后补一次 3s 退避重试:退避在锁外做(不会冻住插件页 3 秒),醒来重取锁并复检
closed/stopping/代次。
⚠️ 这条的真机重启自启实测仍待执行(装本版后重启,看plugin-failures.log与托盘)。代码侧已闭环,真机结论未取。
四、国内镜像与 Gitee(历史欠账)
url.gitee_repo仍是「配了才启用」,默认空;updates::endpoints()在配置后加入 Gitee 候选。sources::picks增 Gitee 附件候选(仅配置时生效),candidates()排序语义与共用 deadline 未改。- 行为变化(请留意):
allowed()从纯前缀比较收窄为「前缀 + 边界字符」。原先配…/FISH-Dock会连带放行…/FISH-DOCK-Plugins(同名前缀),现在只放行边界匹配的地址。只收窄,不放宽。 - 新增
app/docs/domestic-mirror-matrix-2026-11-04.md:六类资源逐项实测。结论摘要——raw 文件是唯一开箱即有免费国内镜像的一类(stable.json在 raw.githubusercontent 与 jsDelivr 三域名字节与 sha256 完全相同);npm 官方与 npmmirror 的 tarball 四项校验全同;pet 图库 DNS 污染级不可达且无任何已确认镜像;Windhawk / LHM 无免费国内镜像。缺口如实标注,不编地址、不要求用户自建服务。 - Gitee 现状(不吹):主仓 Gitee 有 13 个 tags 但没有任何 Release 附件(
releasesAPI 返回[],具体附件 URL 全 404);插件仓 Gitee 的匿名可见性两次探测结果相反(待人工确认)。因此本版仍以 GitHub Release 为唯一发行源,Gitee 通道保持默认关闭。
五、安装包
| 安装包 | 使用场景 |
|---|---|
FISH-Dock-Setup-2.1.0.exe |
首次安装、基础资源缺失或修复安装,包含完整离线运行时。 |
FISH-Dock-Update-2.1.0.exe |
已有版本且基础资源完整时使用,只含相对 2.0.4 的变更;缺失或损坏时提示改用完整包。 |
精简更新包只对 2.0.4 有效:它是差分包,跨版本使用会缺文件。更早的版本请用完整安装包。
六、已知限制与残留
- 便携版更新入口仍不可用:
updates::exe_name()还在拼历史文件名wakuwaku-v<ver>-windows-x64.exe,而真实发行名是FISH-Dock-{Setup,Update}-<ver>.exe→ 便携版的can_install()恒为 false(安装版走installer_names,不受影响)。列为独立条目。 - npm 两源仍无新版:
catalog/stable.json已写 2.2.0,但 npm 官方源与 npmmirror 的 latest 仍是 2.1.5/2.1.5/2.1.6、2.2.0 tarball 404,只有 GitHub Release 有。插件更新目前依赖 GitHub 一条腿;回退逻辑正好覆盖这种情况。 - 真机未验:卡面尺寸(
getBoundingClientRect)、触控惯性下的滚动锁、深浅主题的.note.ok对比度、以及 #70 的重启自启实测。 .bad无独立颜色规则:整套 CSS 里只有组合选择器带红,所以历史上那些“红色失败”其实不红。属既有观感问题,独立条目处理。- 探活无整轮总 deadline:最坏 3 源 × 6 候选 = 18 次
HEAD;单次 fetch 内共用 deadline,整轮候选没有。 KNOWN_GOOD靠人工纪律维护:表过期后回退能力会静默退化到“只有目录当前版”。hop()的同站点域是逐跳评估,比旧版略宽(仍限 GitHub 自有域 + 目录 sha256/size/manifest 三重校验)。
七、验证
- 整 crate
cargo check --offline --bin FISH-Dock→ exit 0。 - 全量
cargo test并行连跑 3 次:每次876 passed; 2 failed; 23 ignored,两条失败恒为lhm_setup::tests的沙箱 DACL 与 Task Scheduler 环境问题。 - 前端:
node --check三文件 exit 0;前端夹具 466 passed(基线 462)。 node app/scripts/check-hardcoded.mjs→ exit 0(合计 128/128/0、HTTP_URL 70/70/0);对比度无新增违规。- 插件仓
node --test preinstall.test.mjs→ 26/26。 - 改动面:主仓 15 文件 +2083/−175;插件仓 3 文件;
catalog/stable.json未改动。
八、插件侧配套
插件仓同期提交了 #71 预装登记改按基线差集增量补登记(plugins/magpie/preinstall.mjs):marker 升级 schema 2 带 baseline,老用户升级后能拿到 2.1.4 才进基线的 Meoo;状态文件的 registered/added 改为按实际清单计算。该修复要等插件仓发新版才到用户手里,本版宿主不包含它。
FISH Dock 2.0.4
FISH Dock 2.0.4
修订版。宿主为 2.0.4,兼容 插件 2.1.x(Clash 与 Magpie 2.1.3、飞书 2.1.0);不是「宿主与插件同版本」。
这一版收 #63、#65、#66、#67 四条 issue,并回归 #64,主线是插件的两条链路——自启与取资源——
外加插件页交互与外链、Agent 会话 API、OFM 业务解耦与发布工程收口。
一、插件自启:一次尝试一份预算
旧实现的问题不是「慢」,而是账算不清:每个阶段各自计时、失败各自重试,重试预算还会层层叠加,
于是一台冷启动的机器上可能出现「clash / magpie 双超时、内置服务根本没被拉起」。
- 自启收进单一调度器
plugin_start.rs:一次尝试共用一份预算(冷启动 60 秒、热启动 10 秒),
整轮生命周期 180 秒封顶,首次尝试加最多 3 次重试(0.5 / 2 / 5 秒),最多两插件并发。 - 每个插件同时只有一次在途:在途 token 由 id、代次、版本、摘要组成;迟到的结果按代次丢弃,
不会回填到新一轮卡片上。 - 到期仍未就绪的尝试记为失败并标
late,不再冒充成功;到期后不再开启新一轮。 - 备份暂停不再二次拉起:暂停时把在途的插件一并 retire 并把代次推走,恢复后旧 worker 回来只能被丢弃,
不再出现「后到的实例覆盖并杀掉前一个健康实例」。 - 失败分类拆成永久(清单/摘要/缺失、身份不符)与瞬时(读文件、元数据失败):前者不重试,
后者可重试,不再靠文案猜。 - worker panic 会被兜底回收槽位;手动启动成功后清掉本轮调度记录,卡片立刻跟实时状态。
二、资源获取:一层择源,一条总预算
- 新增
sources.rs作为唯一取资源层:原站地址永远第一优先,镜像按配置作为候选,每条候选再按可用
代理路重试;候选 × 代理路共用一条总预算,换路不重新放满时间。 - 落地只经唯一临时文件 + 原子改名;失败清掉半截文件,错误按「已试过哪些源、为什么失败」聚合上报。
- 更新包下载同样走这一层:镜像失败回原站,进度与「只保留当前版本」的语义不变。
- pet 的列表、详情、立绘与预览改由宿主取回并缓存,页面只拿本地 asset URL,不再自己外联。
- PawnIO 仍保留固定 SHA-256 与允许主机约束;LHM、Windhawk 沿用各自的校验与安装流程。
- Gitee 通道配了才启用;加速前缀默认为空。
说明:本轮没有为 pet、Release、Windhawk、LHM 找到可确认的免费国内镜像,因此「双源」目前是
「原站 + 可选镜像候选」的能力,不是「已配好两套源」。这一点在下面的「已知限制」里再列一次。
三、pet 的更新提醒
检查到宿主有新版本时,pet 会主动弹提醒,而不是只在设置页里等用户自己翻。
四、插件页:#63 交互与检索
- 「更多」从弹出菜单改成整行可点的动作列表:纵向两行、每行 32 px、行间发丝分隔,
日志在上、卸载在下;卸载常态用危险色、悬停只加 8% 底色。 - 搜索框占位改为短句示例,命名规则移到说明行,长内容省略显示。
- npm 发现式检索补齐分页契约:后端返回
total / filtered / hasMore / nextFrom,
前端按nextFrom推进;短页(如 20 条里只给 5 条)不再出现 0 → 20 → 40 这种跳号或死循环,
被过滤的整页也会正常前进。
五、#67 插件页外链
插件页原来是 sandbox="allow-scripts",没有 allow-popups 也没有 allow-top-navigation,
页面里任何 <a href>、target=_blank、window.open 都点不动。
- 采用宿主受控通道,不放开 iframe 沙箱:桥拦截
a[href]点击(要求真实用户手势isTrusted),
把 http/https 链接交给宿主dock.open_external;宿主先过页面票据,再复用既有的 URL 判定
(只认 http/https、必须有主机、禁 userinfo、禁 NUL)后才调系统浏览器。 file:、javascript:、data:、vbscript:一律不拦也不打开;程序化调用需在用户激活窗口内;
同一链接短时内重复点击会被节流。- 通用
FishDock.call('dock.open_external', …)被列入保留名单直接拒绝——否则脚本可以绕过手势与节流。 - 沙箱属性保持
allow-scripts不变;服务页 iframe 的allow-popups是既有业务窗口,未改动。
六、Agent 会话 API 与 agent-core
- 新增本地 crate
agent-core(只依赖serde_json),承载中性事件与「消息 → 事件」投影;
各 agent 的磁盘 I/O 仍在宿主。 - 新增
/v1接口:/v1/info(免鉴权,可带proof防重放)、/v1/sessions(Bearer)、
/v1/events(SSE,带epoch:seq游标与环形缓冲)。 - 隐私边界:
/v1用独立投影,恒不外露detail/step/reply/error/cwd;关闭详情时清空历史环、
队列与 replay,旧订阅收到reset后结束;所有响应带Cache-Control: no-store。 - 资源上限:SSE 活跃写线程单独计数(≤8),慢客户端按真实 socket 写超时收回;退出时唤醒阻塞写。
- token 落在私有目录(Windows 按当前用户 SID 收紧并回读校验,Unix 0600/0700),轮换走临时文件 + 原子替换,
失败保留旧 token。
七、OFM 解耦、命名清理与发布工程
- OFM 业务彻底解耦:判定集中到
legacy_ofm.rs,开发目录不再接受ofm,旧网关插件走单一特判,
安装器对退役载荷前缀硬拒;资源清单里 OFM 条目为 0。 - 活动上游命名迁移到新名,集成脚本、邮件集成与示例统一为 FISH Dock 命名。
- 配置收口到
config.rs单一入口,新增键全部支持FISH_DOCK_*覆盖;旧前缀只读兼容。 - 发布工程:路径扫描同时覆盖 UTF-8 与 UTF-16LE;release 二进制不再内嵌本机构建路径
(env!("CARGO_MANIFEST_DIR")在 release 下为空),构建脚本不再在未设 vendor 目录时传空参。 - 对比度门禁回到 0 违规;
check-hardcoded与check-contrast均接入测试。
八、已知限制与未验项
以下项目没有在本次自动化里被证明,需要真机或后续确认,不应读作「已通过」:
- 真机 WebView2:原生首帧时序、40 ms 决策窗口、审批气泡二次生长;插件页外链在真机上点开系统浏览器
只验到「桥发出 → 宿主判定 → 调起接口」。 - 登录与开机自启:真实登录流程与「开机后首次自启」需人工重启配合,本次未做。
- 升级与回滚:2.0.3 的 Setup 包本体在本机缺失,只做了合成/隔离环境的升级链路验证。
- 免费国内镜像缺口:pet、Release、Windhawk、LHM 均无已确认的免费国内镜像;Gitee 通道需自行配置。
/v1的运行期细节:no-store原始响应头断言、「阻塞写中退出」的组合用例仍属补充验证项。- token 私有 ACL 的现场核验:自定义数据根(
FISH_DOCK_APP_HOME/--data-root)下的实际 ACL 回读
需要在真机上确认一次。 - Codex Desktop 内部请求现场:分类与过滤按真实 rollout 字段实现,但未在 Desktop 发起内部请求的那一刻
同时抓取 hook 与 rollout。 - 产品壳宽度:固定 520 px 的产品壳在 480 px 视口下会横向溢出,属既有产品决策,本版未改。
九、安装与校验
| 安装包 | 使用场景 |
|---|---|
FISH-Dock-Setup-2.0.4.exe |
首次安装、基础资源缺失或修复安装,包含完整离线运行时(约 312 MB)。 |
FISH-Dock-Update-2.0.4.exe |
已有版本且基础资源完整时使用,只下载本次变更(约 30 MB);缺失或损坏时提示改用完整包。 |
下载后请用同版本的 SHA256SUMS.txt 或包旁的 <exe>.sha256 核对摘要。
同版本号修订不会自动提示更新,请重新下载安装并核对最新摘要。
安装包当前未做 Authenticode 签名。
FISH Dock 2.0.3
FISH Dock 2.0.3
补丁版。宿主为 2.0.3,兼容 插件 2.1.x;不是「宿主与插件同版本」,安装时请按各自的版本号对待。
这一版是上游同步 + 插件启动链路修复,两半:
- 上游同步:把 Wakuwaku 从
27104eab(v0.3.8)推进到ab0eb65a(v0.3.9),按「本仓是否已有实现」
逐项判定后纳入四项修复,另修正两处与版本位有关的既有缺陷(来源清单里一条自 2.0.1 起就写错的选择项、
plugin.json漏掉的版本位)。上游该版共 11 个提交(8 个非合并 + 3 个合并)、16 个文件、+211/−67;
其中两项本仓早在 2.0.1 之前就自己做过且更严格,不重复移植。 - 插件启动链路(任务书
plan-dock-2.0.3.md,诊断
plugin-startup-diagnosis-2026-11-02.md):任务书十项里落了九项
——T01、T03、T04、T05、T06、T07、T08、T09 与维护线程的互斥收口(t19);T02 留 2.1.0(见「已知限制」)。
起点是两条用户可见的病症:「启用自启动后首次启动插件拉起异常」与「设置页连开十次起十个node.exe」——
第一条不是「慢」,是误杀(见下)。
探针超时不再杀进程(T01,诊断 A1)
plugin_process.rs拆出alive()与exited():running()仍然表示「存活 + 握手可应答」,
但不再被当成死亡证明。plugin_runtime.rs的view()因此只在exited()真的给出退出码时
才remove实例、记一条失败。- 修的是:以前
!running()就直接self.processes.remove(id),而Process的Drop会
child.kill()——插件进程还活着、只是那 2 s 探针没应答,就被判死并强杀,连它拉起的
magpie.exe/mihomo.exe一起带走。plugin-failures.log里那批exitCode:null的记录
就是这么来的。冷启时 I/O 与杀软最重、探针最容易超时,所以它只在「开机自启的首次启动」出现,
之后「恢复服务」又正常。 - 这一轮没应答时新增
unresponsive字段,插件卡显示「未响应,正在重试」——它不再冒充「启动失败」,
也不覆盖运行态,下一轮重绘自己会好。
Node 探测放宽并缓存(T05,诊断 A5)
plugin_dependencies::probe的死线 2 s → 8 s(冷启那次是被 Defender 与磁盘占走的,不是 Node 慢)。- 成功的探测按
(路径, 长度, 修改时刻)缓存在进程生命周期里:设置页每次重绘都要走一遍
discover(),以前每轮现起一次node --version,连开十次就是十个进程;现在同一个二进制只探一次,
换了版本或原地升级会因长度/时间变化自动重探。失败不缓存(用户装好 Node 后下一次重绘就该认出来)。
审批快捷键(上游 #97,3efcc47d)
- ZCode、DeepSeek Harness 的审批与计划,
Alt+Y/Alt+N现在能直接回答,和按钮一样。 - 之前
canAnswerWithHotkey把这两个应用(IN_APP)排除在外——IN_APP本来只管「去 XX 处理」
那颗按钮的措辞;结果是按钮能答、快捷键只开岛,按钮上也没有组合键提示。 canAnswer === false(明确答不了)仍然只开岛;问题(question)仍然不替人答。- 回归:
app/test/shortcuts.test.js原来钉的是旧行为(zcode只开岛),改为钉新行为,
并补一条canAnswer === false的反例。
全屏判据(上游 #98,c019eb70 + 7e8af4a7)
fullscreen.rs新增fills(style, zoomed, rect, monitor):没有标题栏的,盖住整块显示器就算全屏;
样式上有标题栏的,「盖住整块且没有最大化」或「窗口矩形恰好等于显示器」才算;最大化的永远不算。- 修的是 Kazumi 这类播放器:它全屏用的是
window_manager 0.5.1的setFullScreen,保留
WS_CAPTION、靠WM_NCCALCSIZE不画边框、把窗口设成显示器的rcMonitor。以前「带标题栏一律不算
全屏」,她的任务栏就压在视频上,底部、顶部都是。 - 新增
what_fills_a_display:七种真实窗口形态(Kazumi 全屏、全屏前已最大化、任务栏顶部时被挪到
每边 +11 像素、差一像素、两种最大化、HELLDIVERS 2 的每边 +1 像素、副屏)各一条断言。 - 本仓原有的截图工具豁免(
SKIP_PROGRAMS六项:SnippingTool、ScreenClippingHost、ScreenSketch 等)
与工作区自愈resolved()一并保留,未被上游版本覆盖。
灵动岛跟随槽位(上游 #98,ba4c6cab)
- 任务栏左、中、右任一段的宽度变了(托盘图标几秒后才陆续到、之后新增一个、监控的数字变宽),
她的胶囊跟着重新对齐。 - 之前胶囊按「画任务栏那一刻」的位置算
--isl-x,之后槽位被挤走它却留在原处,压在监控上、
严重时压住托盘的箭头,要展开一次胶囊才归位。 - 做法:对三段
zones挂ResizeObserver(island.js的slotWatch),只在
「任务栏已建 + 在岛里 + 家在任务栏」时重放placeSlot();横向与侧边两条路都走这一个入口。
退出时崩溃(上游 #100,9ca1c269)
jump.rs的console_window:找命令行会话的窗口时要临时AttachConsole到那个会话的控制台,
断开(FreeConsole)会把她自己的标准句柄一起关掉。这些句柄号之后被她打开的文件重用,
任务栏模式下退出时tray-icon往「错误输出」写一句,写不进去(os error 5),eprintln!当它是
panic,进程就这么结束了(上游 v0.3.8 的 0xCFFFFFFF 一类退出)。- 现在连之前先
GetStdHandle记下三个(STD_HANDLES:-10 / -11 / -12),断开之后
SetStdHandle原样放回去。发行版本来没有标准句柄,放回的就是空,行为与从前一致。
宠物图库走代理的说明(上游 #99,513f9979)
- 代码不移植:本仓
fetch.rs自 2.0.1 前(6376a35)就自己实现了这条路,而且更严格——
走proxy::web_ways、按pet_timeout的预算、http_status_as_error(false)、站点地址走
config::pet_site()、错误分network/http/notFound三档、另有离线读缓存兜底。 - 补的是说明:设置页「网络代理」的中英文案(
s.net.systemNow/s.net.manualNote)原来列了
软件更新、崩溃报告、插件联网、LibreHardwareMonitor,唯独漏了宠物图库;proxy.rs与
docs/DEVELOPMENT.md同样补上。 - 补的是测试:上游那条「图库真的照网络代理走」的断言本仓没有,补为
fetch.rs的the_gallery_goes_the_way_the_network_proxy_says(起一个本地代理,钉住它收到的是
CONNECT codex-pets.net:443 HTTP/1.1)。属特性回归(characterization),不是本轮修复的证明。
不移植的两项(本仓已有)
- 任务栏「抢任务栏」去抖(上游 #98,
efbcfb52):本仓taskbar.rs的Tug早就有同一个守卫
(self.seen > 0 && self.at[(self.seen + TUG_TIMES - 1) % TUG_TIMES] == tick→ 同一个 tick 只算一次),
自6376a35起如此。上游的写法与它等价,改了只是换个写法。 - 图库走代理的实现:见上一节。
资源校验改文件指纹(T06,诊断 B1)
state/verified从「两行:清单摘要 + 资源目录」改成一份 JSON:{schema, resources, files: {path: {bytes, mtime}}}。- 启动判据
pending()逐 manifest 文件比 size+mtime,只对指纹变了的文件重算 SHA-256(verify_pending_at);
read_manifest不再对manifest.json算摘要,因此换一份清单不再等价于全量重校。 - 旧两行格式解析失败即按「没有记录」处理(等价于升一次级自愈),不写迁移脚本;
用例the_old_two_line_marker_counts_as_missing。 - 标记写入改成原子替换:同目录临时文件
.verified-<pid>-<n>→write_all + sync_all→storage::replace,
任何一步失败只删临时文件、旧记录原封不动,并印一行「旧记录保留(下次启动再校验)」。 - 用例:
the_start_checks_the_required_files_and_remembers_a_fingerprint_per_file、
one_changed_file_is_verified_again_and_the_unchanged_ones_are_not、
a_new_manifest_alone_does_not_ask_for_the_tree_again、
the_marker_records_the_stamps_the_judgement_saw_not_a_later_stat、
a_failed_write_of_the_marker_keeps_the_old_record。
全量校验移出首帧(T07)
- 同步半
prepare_at只做清单校验与必需文件symlink_metadata;逐文件哈希交给后台线程。
后台校验失败与从前一样show_failure + exit(1),只是晚几秒而不是挡住首帧。 - 用例
the_start_up_path_hashes_nothing_and_a_damaged_file_is_named_in_the_background、
the_background_half_is_told_where_and_says_so_when_it_is_not。
WebView2 授权按需(T08)
- 只在校验通过、且判据认为缺权限时做一次,不再每次启动递归授权:判据是目录与
msedgewebview2.exe两处 DACL 的启发式
(含S-1-15-2-1在本机被写作别名AC的情况),不逐文件试读。 - 用例
the_webview_permission_is_written_only_where_the_sandbox_cannot_read、
a_refused_grant_for_a_new_directory_is_not_remembered。
插件校验缓存(T03)
- 新增
host/plugin-verified/<id>-<version>-<digest>.json,逐文件记 size+mtime。verify()改成快照优先:
plugin.json照读 → 逐文件 stat → 全等即通过(一个字节都不进 SHA-256)→ 只有变了的文件才重算并回写快照。 - 清理面:
cleanup()增prune_snapshots()(只留当前版本与当前摘要,别的插件不碰),uninstall()收掉该 id 的整族快照。 resume()不再同步跑cleanup()/cleanup_staging(),改由启动 30 s 后的维护线程maintain()做。- 用例
verification_is_cached_by_size_and_mtime_and_a_missing_snapshot_hashes_again、
snapshots_follow_the_active_version_and_vanish_on_uninstall、upgrade_leaves_only_the_current_version_and_current_digest。
插件页面入口放锁(T09)
open()去掉同步verify;present()只查实例表,把目录、清单与指纹表记进 Grant。
call()与gateway.open走新的call_outside():借Arc<Mutex<Process>>句柄、放锁等回答、回来按代次结账;
等待期间被停用或卸载的实例,其回答被丢弃,不写进新代次的失败记录。resource()不再读注册表(无 EDIT 锁)、不再每资源哈希:指纹命中直接发,变了才重算那一个文件。- 实例表改成
Arc<Mutex<Process>> + generations,view()用try_lock(在途 RPC ⇒ 活着),event()跳过,
stop_all()立 stopping 闸;T01 的 Liveness 判据与liveness()原样保留。 - 用例
resources_are_served_from_the_grant_fingerprint_without_rehashing、
page_grants_bind_identity_version_enabled_state_and_lifetime、
an_in_flight_page_call_is_discarded_once_its_instance_is_retired。
冷/热分档超时(T04)
- 冷态判据
config::plugin_startup_cold:登录后 0–300 s(含端点)算冷;取不到登录时刻(非 Windows / WTS 查询失败)
或时钟回拨算热。 - 调用超时热态 5000 ms、冷态 20000 ms,
plugin_host_call_timeout_ms_of夹在 1–30000 ms;同一组数经host.hello
的hostCallTimeoutMs下发,插件侧缺字段回落 5000(SDK 注释同口径)。 - 冷态
Process::start失败重试一次(500 ms 退避),热态不重试;错误文案带「(冷启已重试一次,首次:…)」。 - 用例
cold_window_is_five_minutes_with_none_and_rollback_as_warm、
a_cold_start_retries_once_and_a_warm_start_does_not、
a_cold_failure_retries_exactly_once_and_a_warm_one_does_not、a_failed_start_removes_the_new_version_directory(gated)。
维护线程与安装的互斥收口(t19)
- 模块级上层串行锁
MAINTENANCE:install()/uninstall()/maintain()/set_autostart()/
set_enabled_starting()五个入口先过它,锁序CURRENT → MAINTENANCE → EDIT_REGISTRY → EDIT。 - 修的是「维护线程把正在写入的新版本目录当过期版本删掉」:改动前 cleanup 与 install 同在
CURRENT下天然互斥,
挪到线程后这条互斥丢了。 - 为不造自死锁(
std::Mutex不可重入),两个开关入口各拆出set_autostart_locked/set_enabled_starting_locked
无锁体,命名即契约:锁内体绝不回头调用会再次取锁的公开入口。 - 结算段(
spawn_starts)锁内只剩install_instance(&id, generation, process)——签名里没有Manager,
想验哈希或写注册表在类型上写不出来。 - 用例
maintenance_waits_for_the_install_lock_before_it_prunes_anything、
the_toggle_entries_wait_for_the_maintenance_lock、
a_pending_journal_is_recovered_under_the_lock_and_the_new_version_survives_maintenance。
插件页卡面重做(t11)
每张卡按「一台在跑的服务」重排:左身份(名字)→ 中信号(状态灯 + 运行态 + 版本迁移)→ 右操作(一颗实心)。
| 位置 | 改前 | 改后 |
|---|---|---|
| 状态 | 3 个并列标签(版本 / 运行中 / 有更新) | 1 条 ● 运行中 · v2.1.2 → v2.1.3;同号重发写「v2.0.0 → 同号新版」,不能更新时不画箭头 |
| 卡内按钮 | 5 颗同高(更新 / 关闭自启动 / 停用 / 卸载 / 打开插件日志) | 4 颗(更新实心 + 关闭自启动描边 + 停用描边 + 更多),卸载与日志收进「更多」 |
| 实心按钮 | 红色的「关闭自启动」(另有绿色「去 Star」) | 卡内唯一实心是「更新」;红色只留卸载的 hover/focus |
| 元数据 | 2 行正文级(路径、安装来源各一行) | 1 行 12px 次级(路径与来源同排) |
| 内容行 | 带功能页的卡 4 行 ~145px、纯服务卡 3 行 ~92px | 3 行 ~117px(−19%)、2 行 ~77px(−16%) |
| 功能页入口 | 与按钮同款的小胶囊 | 32px 高、带 › 的整行入口 |
| 视图切换 | 3 颗按钮靠 aria-pressed 假装切换 |
真 tablist:aria-selected + roving tabindex + ←/→/Home/End,与 tabpanel 关联 |
| 检索 | 与八个按钮同权重的「检索」 | 输入框内的字段按钮,回车即搜;输入框补 aria-label |
高度与行数是按 CSS 盒模型与渲染出的 HTML 结构算的,没有截图核对,见「已知限制」第 10 条。
来源清单与版本位修正
UPSTREAM.json的version→2.0.3,selectedUpstreamThrough→ab0eb65a,
selectedChanges补本轮四项。- 删掉一条一直写错的:
"agy approvals read globalPermissionGrants from config.json (upstream #91, 5a0fee56)"
自 2.0.1 起就挂在「已选」里,但本仓没有needs_approval/classify_tool/globalPermissionGrants
任何一处代码(events_agy.rs317 行,上游同一文件 1115 行),它没有可附着的审批子系统。
现移入excludedChanges并在 notes 里写明原因,对应 #51。 integrations/claude-code/.claude-plugin/plugin.json的版本位停在 2.0.1:2.0.2 只改了
Cargo.toml/Cargo.lock/tauri.conf.json三处,漏了它,于是updates.rs的
one_version_everywhere在 2.0.2 上一直是红的(它断言四处说的是同一个版本)。本版补到2.0.3。
已知限制
下列 12 条全部如实保留,其中第 1、9、10 条决定了这一版能承诺到什么程度。
- T02「插件 → 宿主」回调解耦仍留 2.1.0:它是 L 级结构改动,需要 t29 / t30 / G1 全组重跑,本版没动。
plugin_api.rs的 install 提交段仍持CURRENT:T03 只把Runner侧的重活挪出锁,API 提交段这一小段仍在全局锁里(t13-F2、t17-F5)。- 冷态重试在「用户点启用 / 装完即启」路径仍占
CURRENT:这两条走的是同步Process::start,冷窗最坏约 40.5 s(20 s 首试 + 0.5 s 退避 + 20 s 重试),期间插件页的其他操作要等。 is_running对「已死但实例锁被占」的实例会短暂答true:取的是 T01 同向取舍——try_lock抢不到即视为在途 RPC,错在少杀而不误杀,下一轮重绘自愈。sandbox_may_read只判 ACE 类型、不判权限位:只认 DACL 里有没有对应的 ACE 条目,不去核实它给的是RX还是别的位。- 后台校验失败走
show_failure + exit(1)硬退:T07 把全量校验挪出首帧,但失败仍是硬退(与 2.0.2「校验通过才启动」同语义),只是晚几秒。 - ...
FISH Dock 2.0.2
FISH Dock 2.0.2
补丁版。宿主为 2.0.2,兼容 插件 2.1.x(插件 2.1.2 稍后单独发布);不是「宿主与插件同版本」,
安装时请按各自的版本号对待。
这一版不引入新功能,只修插件的三件事:看不见(失败没有日志)、查不到(错误只剩一句兜底话术)、
停不掉(残留内核被当成自己的进程杀错)。逐条对应到来源 issue。
插件诊断(#53 / T01–T03)
- stderr 不再被丢弃:插件进程的
stderr从Stdio::null()改为管道接管,逐行写到
host/plugin-logs/<插件 id>.log;单文件 1 MB、保留 2 份滚动,写入前过统一脱敏(令牌、路径、
邮箱、IP、主机名),单行截断 300 字符。写入失败只丢这一行,不盖掉原始错误。 - 失败可查:
host/plugin-failures.log每行一条 JSON,含插件 id、版本、失败阶段、退出码、
时间、原始错误与对应日志路径;宿主重启后仍可查。 - 错误带定位:插件卡的
error文本改为「原始原因 + 退出码 + 日志路径」,
并新增结构化字段failure/exitCode/logPath;无退出码时说的是「无响应(握手超时)」,
不冒充「已退出」。 - 日志入口:插件卡新增「打开插件日志」,直接打开
host/plugin-logs/。 - 日志目录不在整仓备份范围内(
config_backup不收),备份体积不因此增长。
运行态与标签页(#14 宿主侧 / T04–T05)
- 运行态判据:
running()从「进程存活」改为「进程存活 + 握手可应答」——发一次host.hello,
任何编号匹配的应答都算活着(2 秒超时、正结果缓存 2 秒)。进程还在但事件循环卡死时不再判为运行中。 - 残留内核回收:按 pid + 实际启动时刻(±5 秒)+ 控制器可达三重判定,只回收「连不上控制器」的
僵尸内核,能连上的留给插件 attach;不根据持久化 pid 结束外部程序。 - 标签页不再消失:插件页标签的显示条件从「插件正在运行」改为「已安装 + 已启用 + 已解锁」。
插件进程退出后标签页仍在,点进去显示错误态(原因 + 「启动」+「打开插件日志」),启动后当场恢复。
这修的是「退出再进 Dock,Clash 标签页没了」这类现象。
错误码透传(#14 / T12)
- 插件侧抛出的业务错误不再被折成一句通用文案。三处折叠已打通:
sdk/node/plugin.mjs的命令包装、
宿主plugin_protocol::answer()、插件页sdk/browser/bridge.js。 - 页面按
code查表显示专属文案(含「下一步做什么」),未知码才落到唯一的「未识别错误」。 - 兼容旧行为:
publicServiceError仍返回字符串(gateway.mjs依赖);业务错误不再被当成致命错误
停掉插件进程(is_business_error(&error) || error == OPERATION_ERROR)。
版本位
Cargo.toml、tauri.conf.json、Cargo.lock与安装器--version统一为 2.0.2。
漏掉Cargo.lock时cargo check --locked会直接报cannot update the lock file,本版一并修正。
已知限制
- 真实 mihomo 内核的 attach / 停止、真实 Windows 注册表读写路径、3 秒 attach 冷却的真机手感,
均未在本轮自动化里覆盖,属 G1(双插件自启高压测试)与 G2(成包校验)的范围。 - Clash 侧部分启动失败的
operationError仍是固定文案(例如端口冲突时页面显示「启动失败,请检查
内核路径、端口和订阅缓存」而不是精确码)。可归因信息在同一次启动的插件日志里;精确码化列入
下一版候选,不算本版缺陷。
安装
FISH-Dock-Setup-2.0.2.exe(约 312 MB):首次安装、资源缺失或修复安装。FISH-Dock-Update-2.0.2.exe(约 30 MB):已有版本且基础资源完整时使用,只含本次变更。- 两个包都附同名
.sha256,另有SHA256SUMS.txt汇总;安装包当前未做 Authenticode 签名。
FISH Dock 2.0.1
FISH Dock 2.0.1
维护版。宿主为 2.0.1,兼容 插件 2.1.x(插件仓已到 2.1.0);不是「宿主与插件同版本」,
安装时请按各自的版本号对待。本版按用户能感知的变化整理,逐条对应到来源 issue。
设置页与公告(#39)
- 公告有内容时也提供「刷新」入口,不再只在空状态才给。
- 打开「关于」页超过 5 分钟后自动重新获取一次公告(原来只有一小时门)。
- 公告标题不再重复版本号(Release 名自带版本号时去重显示)。
全局快捷键(新能力,#45)
- 设置页新增「全局快捷键」录制节:为动作录入组合键。
- 组合键被其他程序占用时给出提示;30 秒内未录到可用组合就放弃,不静默挂起。
插件平台(#37 / #38)
- 更新 / 卸载改为事务:中断可续做,校验不过回滚,失败不阻断其它操作(#37)。
- 回滚点可选择性导出;更新成功后旧版本目录清零,只留当前激活版本(#37)。
- 卸载彻底:清掉安装载荷与注册,但保留用户配置,不误删数据(#37)。
- 插件自启动在 Dock 运行中拨开关即时生效;
resume()失败不再整批静默跳过(#38)。
窗口与任务栏、显示(#48 P2 / #43 / #44 / #47)
- 无标题窗口也会出现在任务栏(不再因没有标题被漏掉)(#48 P2)。
- 托盘图标点击可到达自身(左键/右键都有响应)(#48 P2)。
- 右键菜单精简:她的菜单只说关于她的事,任务栏自己的项(任务管理器、Windows 设置等)
留在条带自身的右键菜单,不重复(#48 P2)。 - 工作区自愈:换显示器或改缩放后,最大化窗口不再停在条带上方;副屏条带移动不误改主屏(#43)。
- 文字 110% 叠加显示缩放时,任务栏部件不再上浮、卡片画面不再偏小错位;任务栏按 Windows
同厚度呈现(#44)。 - Codex:失败命令计为失败;「Approve for me」交还 Codex 处理(#47)。
崩溃(#40 / #41)
网络韧性(#42)
- Wi-Fi 没有出口时读取瞬时为空不再中断(同形加固,#42)。说明:本仓是同形加固,
不是「修复了一个闪退」——避免把它读成上游那条独立故障。
商店与模型能力
- OFM 本地模型正式下架:商店目录与宿主三处白名单同步移除;已装用户不受影响,
仍可正常启用/卸载(商店目录调整,依用户要求,无单条 issue)。 - 模型能力由 Magpie 插件提供:关于页与 README 的致谢同步更新,不再把模型能力记在
OFM / Our Free Model 名下(文案调整,依用户要求,无单条 issue)。
明确不包含(转 2.1.0)
兼容性
- 宿主 2.0.1 与 插件 2.1.x 配套;插件版本号高于宿主,属正常。
- 双包:
FISH-Dock-Setup-2.0.1.exe(完整安装/修复,190 MB)与
FISH-Dock-Update-2.0.1.exe(已有安装且基础资源完整时只下载变更,31 MB)。 - 数据目录、注册表身份等沿用 2.0.0 的约定,本版不再迁移。
FISH Dock 2.0.0
FISH Dock 2.0.0
宿主升到 2.0.0:所有版本位对齐,旧发行名按「沿用 / 迁移 / 清理」三档收尾,用户
数据目录迁到 FISH Dock 之后旧目录直接删除。宿主与插件同批发布,四个官方插件
也全部 2.0.0。
版本锚点
一处改了别处没跟上,是这次最容易被测试抓住的地方。2.0.0 起下面这些位置写的是
同一个版本号,updates::tests::one_version_everywhere 盯着前两项:
| 位置 | 作用 |
|---|---|
src-tauri/tauri.conf.json → version |
Tauri 元数据,安装器 ProductVersion 取自这里 |
src-tauri/Cargo.toml → package.version |
宿主二进制版本(CARGO_PKG_VERSION) |
src-tauri/Cargo.lock → name = "wakuwaku" |
锁文件里的自身条目,随 Cargo.toml 同步 |
integrations/claude-code/.claude-plugin/plugin.json → version |
Claude Code 集成清单,同一条测试比对 |
README.md / README.en.md / Wakuwaku/README.md / Wakuwaku/README.en.md |
版本徽标、下载与更新记录链接 |
tools/check-readme.mjs |
不再自带版本字面量,改读 tauri.conf.json 再校验四份文档 |
双包
v2.0.0 照旧发布两个包,校验值在同一条 Release 里:
| 安装包 | 使用场景 |
|---|---|
FISH-Dock-Setup-2.0.0.exe |
首次安装、基础资源缺失或修复安装,含完整离线运行时 |
FISH-Dock-Update-2.0.0.exe |
已有安装且基础资源完整时只下载变更文件;缺资源会提示改用完整包 |
旧名三档
改名的风险不在「改」,在「改错档」:动到用来匹配的名字,升级后的程序就认不出
自己写过的东西,安装器也清不掉旧值。所以按「动了会不会丢用户数据、会不会让升级
找不到已登记的安装位置」分成三档。#t16 + #t18 之后,原来的「沿用不动」一档已经
不存在:用户看得见的名字全部换成 FISH Dock,旧名只留在「读取/识别」这一侧。
已改名(写出的一律是新名)
- 主程序文件名
FISH-Dock.exe(Cargo 包名fish-dock,[[bin]] name = "FISH-Dock"); - 应用标识
com.fish.dock,通知 AUMID 同步(notify.rs的APP_ID); - 任务名
FISH Dock at logon-<SID>、传感器任务FISH Dock sensors-<SID>; - 命令行开关一律写
--fish-dock-*(12 个:ensure-running / codex-hook / agy-hook /
zcode-hook / dsh-hook / reasonix-hook / out-of-job / hw-dump / lhm / sensors /
sensors-setup / sensors-remove,另加 at-login 与 uninstall-cleanup); - 传感器助手
FISH-Dock-sensors.exe,助手目录%ProgramFiles%\FISH Dock\sensors; - 用户数据目录
%LOCALAPPDATA%\FISH Dock、卸载表登记名FISH Dock、Run 值名FISH Dock。
仍兼容读取(旧名只读不写,至少留一个版本)
- 旧 exe 名
DSH-FISH.exe、wakuwaku.exe、wakuwaku-sensors.exe:安装器与更新器按旧名
识别并删除(LEGACY_EXE_NAMES/EXE_NAMES),有清单的安装也会补扫; - 旧开关
--wakuwaku-*与更早的--claude-pets-ensure-running:src/flags.rs的别名表
在进程入口归一成新名;旧 hooks/旧任务里的写法仍能跑,connection.rs在修复/连接时把
它们改写成新名(is_ours认新旧两套); - 旧任务名
Wakuwaku at logon-<SID>/Wakuwaku sensors-<SID>、旧 Run 值Wakuwaku、
旧卸载表登记DSH-FISH/Wakuwaku:只认、修正与删除,不再写; - 旧占位符
__WAKUWAKU_PORT/EXE/DATA__:omp 扩展模板与新名并存、两套都填。
明确不改(外部身份,不是本产品的名字)
- Claude Code 插件 id
wakuwaku@wakuwaku与其 marketplace 命令:已发布到插件市场,
改了等于让所有人的已装插件失效; integrations/下的文件名(wakuwaku.js/wakuwaku.ts)与 omp 的extensions/wakuwaku.ts:
其他 agent 按文件名加载,改名会让它们找不到;- 事件上报里的
from=wakuwaku埋点:远端统计与崩溃去重按它归属,属于对外协议; - 上游许可文件名
licenses/Wakuwaku.txt:这是许可方的名字,不是我们的。
迁移:用户看得见的旧名 → FISH Dock
- 任务计划里给人看的作者与说明(
logon.rs的task_xml/DESCRIPTION); - 用户数据目录
%LOCALAPPDATA%\DSH-FISH→%LOCALAPPDATA%\FISH Dock(见下一节)。
任务名本身(Wakuwaku at logon-<SID>)不改:它是用来匹配的,不是给人看的。
清理:旧发行名留下的登记与文件,安装时按归属删掉
- 旧主程序
$INSTDIR\wakuwaku.exe:安装段就删,不等卸载(删旧建新,不留旧 exe); - 指向旧 exe 的 Run 值,与它在
StartupApproved\Run里的同名标记:只按
「本安装目录 + 我们自己的旧文件名」精确匹配整条值文本,别的程序写的同名值不动;
指向当前主程序的那条是用户的选择,保留; - 旧卸载表登记项(
Wakuwaku、FISH Dock):登记位置就是本目录、或显示名是我们
自己用过的旧名,才整键删除; - 卸载表里那条带旧发行名的
ProductName:2.0.0 起只用DisplayName+
DisplayVersion。
卸载段再兜一遍底:Run 值、同名标记、旧 exe、旧登记项都按同一套归属判定清理,
不递归删目录、不碰用户数据。旧版回滚点不再保留 —— 只留当前版本。
用户数据目录迁完即删
安装器带上 --migrate-home 才会动用户数据目录;不带这个参数(包括开发版直接跑
install-update.cjs)一个目录都不碰。迁移顺序是「整树盘点 → 逐个复制 → 逐个核对
SHA-256 → 两份清单对齐 → 才删旧目录」,所以:
- 迁完:旧目录直接删除,不留回滚副本;
- 没迁完(新目录已存在、复制中断、摘要不符、旧目录不在):旧目录原样保留,程序
照常可用,安装器只提示「完全退出后重跑一次安装程序」; - 旧目录被 WebView2 占用删不掉:数据已经在新目录里核对过,不算迁移失败,旧目录
留下并记一行,交给用户手动清; - 树里有链接或非普通文件:拒绝迁移,不跟着链接把树外的东西算进来。
安装器读 home-migration-failed 标记必须排在删除暂存目录之前 —— 失败分支同样会
把暂存目录整份删掉,读晚了就什么也读不到。
兼容性
本版是 breaking:只支持 Dock ≥ 2.0.0 与同批发布的插件 2.0.0。插件目录条目结构
从 author + maintainer 改成 authors[] + note,旧 Dock 读不出新目录,插件页
会是空的;1.x 装 2.0.0 插件同理。升级时请整套一起换。
验证
node scripts/installer.test.mjs:15/15,含「离线 makensis 真编出安装包」(makensis
在本仓的中文路径下写不出安装包,测试把 NSIS 工具树暂存到 ASCII 临时目录再编)。node scripts/installer-upgrade.test.mjs:9/9,含「NSIS 通过真实 Node 和 PowerShell
5.1 完成中文路径安装与覆盖」与 #27-D 的迁完删除 / 未迁完不删 / 重试 / 删旧失败 /
拒绝跟随链接。node scripts/prepare-installer.test.mjs:6/6。node scripts/branding.test.mjs:6/6。node tools/check-readme.mjs:四份文档的版本锚点、相对链接与锚点全部通过。
FISH Dock 1.2.3
FISH Dock 1.2.3
给插件更新收个尾:升完级不再留旧版本目录,正在看的那一页也会换成新票重建;顺手修了主仓两条 issue。
插件更新不再留旧版
以前每次装新版,旧的 packages/<id>/<version>/ 都留在磁盘上:一次升级多几十
MB,旧目录里还带着旧 manifest。现在升级提交成功之后才清理,只保留当前激活
版本和最近写入的那一个(回退点),更老的删掉。失败路径不受影响:装不上时旧版
仍然在,回滚照旧。
同时修了页面一侧的漏洞:插件升级后 digest 变了,旧 iframe 会被移除、旧票据
会被关闭,但页面宿主还记着"当前就在这一页",于是下一次进入直接返回、不重建,
用户盯着空白。现在退场的若是当前页,会清掉这一状态,下次进入用新票据、新
地址重新建帧。
主仓 issue
- #17 密码框加可视按钮:邮件表单与 LibreHardwareMonitor 表单的密码行右侧多
一枚眼睛,点击只切换type,值、光标、表单数据都不动;空值禁用,关闭后一律
回到隐藏;aria-pressed与中英文案齐备,Tab 顺序是输入框 → 按钮。 - #16 安装日志乱码:安装器本身是 Unicode 版,界面文字不乱;乱的是子进程那
一路——nsExec::ExecToLog按 ANSI/OEM 解释node.exe的 UTF-8 输出。现在面
向安装器的输出全部改英文,并统一过ascii()兜底,同时往安装目录写一份
UTF-8 BOM 的install.log存中文细节,卸载时一并删除。
配套
插件页的写操作(签到页的「关 / 开」「立即签到」)需要 Magpie 插件 1.2.0 的页面
调用桥才真正生效;本版与此配套发布,安装后到插件页把 Magpie 更新到 1.2.0。
验证
cargo test --bin wakuwaku:572 通过 / 2 失败(lhm_setup两项,沙箱内
PermissionDenied与计划任务根目录0x80070003,环境性)/ 14 ignored。node --test test/extension-page.test.js:4/4。node --test scripts/installer.test.mjs scripts/install-update.test.mjs scripts/install-update-performance.test.mjs:23/23,含「安装日志只用 ASCII」
的断言。
FISH Dock 1.2.2
FISH Dock 1.2.2
发行版本:v1.2.2。主程序、Tauri 元数据、Cargo 锁文件与安装器统一为 1.2.2;插件保持独立版本。
修复:插件页的「自启动」开关点不动
1.2.1 给插件页加了 host.autostart 接口,但页面桥按设计拒绝 host. 与 plugin. 开头的方法(extension-page.js 里那条校验把这两类名字留给插件后端)。于是插件页调用 host.autostart 时消息被直接丢弃:既不成功也不报错,前端 Promise 一直挂着,开关停在禁用状态——看起来就是「点不动」。
本版把宿主侧接口改名为 dock.autostart,走同一套逻辑(读 Installed.autostart、写回注册表),并保留 host.autostart 作为别名兼容。插件页面同步改用 dock.autostart 后:
- 打开网关页即可读到当前状态,点击立即生效,失败会回滚勾选并在页面上报错;
- 宿主早于 1.2.2 时开关自动隐藏(旧桥会丢弃
dock.以外的命名空间,用它只会静默失败)。
同一批发布里 Magpie 1.1.1 与 Clash 1.0.4 都已改用新接口。
其余
本版只动这一处宿主代码与版本号,认证、备份、插件安装、更新检查的行为与 1.2.1 相同。1.2.1 的完整说明见 release-1.2.1.md。
安装
FISH-Dock-Setup-1.2.2.exe:首装或修复。FISH-Dock-Update-1.2.2.exe:精简更新包,适用于基础资源完整的 1.2.1 安装。- 同版本
SHA256SUMS.txt一并发布。
安装包不改动 %USERPROFILE%\.fish-dock 里的凭据与配置,升级后登录与已装插件都保持原样。
FISH Dock 1.2.1
FISH Dock 1.2.1
发行版本:v1.2.1。主程序、Tauri 元数据、Cargo 锁文件与安装器统一为 1.2.1;插件保持独立版本,不随宿主升级。
本次是修复版,重点在「掉登录」与「备份不完整」两件事,另外合入两个社区 PR。
GitHub 登录:认证一次,之后长期有效
- 凭据不再因版本变化读不出来:凭据记录加
schema_version并去掉字段严格校验,旧记录照旧能读;读不懂时另存github-access.dpapi.bak,不再直接丢弃。 - 失效分级:只有 GitHub 明确回
bad_refresh_token/expired_token/invalid_grant才清凭据;网络失败、限流、边缘节点 401 一律保留会话。 - 启动复核:每次启动(含更新到新版本之后)后台自动续期并复核身份与主仓 Star,不必等用户点插件;失败只停在「待复核」。
- Star 回执 7 天:一次联网核验管一周,离线不把官方插件锁死。
- 接受长期令牌:应用关掉「令牌过期」后签发的是不带
expires_in、不带refresh_token的令牌,现在照收(expires_at记 0,视为永不过期)并清掉不再需要的刷新凭据;仍带 8 小时有效期却缺刷新令牌才算异常。
配置备份:只带凭据与名册,导入不再被版本卡住
- 运行态不进包:
trash、routing、用量日志、模型与市场缓存、*.bak、*.tgz、dist、node_modules统一排除。 - 邮件正文不入包(
host/mail-store);邮件账号名册与登录凭据照常带走。 - 解不开的凭据原样带走:别的用户或别的机器留下的 DPAPI 不再让整包导出失败,按
opaque编码带走并计入「需重新登录」数。 - 导入放宽:只要求
data_schema相等,插件版本升级过也能恢复,目标机器已装的版本号保持不变;不再用备份里的旧host/storage.json覆盖宿主版本。 missingCredentials回真实值,不再固定为 0。
插件
- 自启动开关(宿主侧):
Installed.autostart持久化,启动恢复只看enabled && autostart;插件页可通过host.autostart读写。插件页面上的那个开关要等插件重新打包发布后才出现,安排见仓库外待办。 - 认证只约束官方插件:安装、启用、页面与扩展调用改用
require_plugin,GitHub 与主仓 Star 只对fish.feishu/fish.clash/fish.magpie/fish.ofm生效;第三方插件不受这条约束。 - 第三方插件(新):插件中心新增「第三方插件」页,「官方商店」改名「官方插件」。按 npm 包名检索公开 registry 并安装;包需含
plugin.json与README.md,声明摘要逐一比对,按第三方命名空间登记,与官方插件共用安装事务、备份与回滚。
社区 PR
- #14 灵动岛窗口铺满工作区:窗口宽度恒为工作区宽、左上角固定,中/右锚点切换宽窄页不再横移重绘。
- #15 连接判断与账单分账:设置快照改用完整渠道列表(ZCode、DSH、Reasonix 不再被误判未连接);token 组件新增 ZCode / OpenCode / DSH 三份账,与「今天」卡同步翻页。合入后修掉
selAgent在两块之间互相重置的缺陷,并把 DSH 解析抽成生产与单测共用的一份。
验证
- Rust:565 项通过 / 2 项环境相关失败(
lhm_setup的目录 ACL 与 Task Scheduler 根目录)/ 14 项忽略。 - 前端:35 个测试文件全部通过。
- 新增用例:认证 6 项(多字段记录可读、无过期令牌永不重登、启动复核失败不清凭据、恢复后补齐 Star、长期令牌照收、短期响应缺刷新令牌仍报错)、备份 4 项、第三方插件 3 项、账单 1 项。
安装
FISH-Dock-Setup-1.2.1.exe:首装或修复,覆盖安装会保留数据根与已装插件。FISH-Dock-Update-1.2.1.exe:精简更新包,适用于基础资源完整的已有安装。- 同版本
SHA256SUMS.txt一并发布,下载后请核对。
两份安装包都不改动 %USERPROFILE%\.fish-dock 里的凭据与配置;升级后原有 GitHub 登录继续有效。