Repository navigation
v0.2.8
TeamFlow v0.2.8
发布日期:2026-10-12。变更清单按 release-npm-package 铁律 1 由
git log v0.2.7..HEAD生成
(5 提交 / 25 文件 / +1677 −22,含 5 份通稿文档),不凭会话记忆。无破坏性。
设计决策与五轮 UI 真机反馈的完整论证见docs/adr/0012-update-notice-and-one-click-upgrade.md。
中文
⬆️ 升级要点
- 从 0.2.7 及以前升级:本版起插件会自己提示新版本;本次升级仍需手动(命令见 README「升级(对使用者)」),
之后的新版本就能在工作台 / 插件页里看到并一键升级。 - 升完重启
dsh --profile web(桌面版重启桌面应用)。
① 为什么做这个(ADR-0012 的问题定义)
真机观察:插件已更新多版,仍有不少用户停在旧版本。拆成两轴后结论是——问题不在「装不上」,
而在 ①用户不知道有新版(发现)②升级不是点一下(动作成本)。本版只解决这两轴;
可达性(npm 镜像滞后等)不是本轮问题。
对存量用户的第一次触达无效——通知机制本身要靠升级才能获得 ⇒ 老版本用户看不到它。
撬动存量靠 README / 发版通稿写明升级命令,这根杠杆不能省。
② 发现:三处落点,一份实现
| # | 落点 | 覆盖谁 |
|---|---|---|
| ① | 宿主插件页徽标 + 操作(plugins.detail.badge / .actions 槽位贡献) |
去设置页管理插件的人——找「更新」的默认位置 |
| ② | 工作台顶部提示条 | 打开工作台的人(打开时按需探测一次) |
| ③ | 全局面板头部「检查更新」按钮 | 唯一的手动入口:①②都吃 24h 缓存,用户得有主动问一次的办法 |
三处共用 client/update-ui.tsx(唯一实现,不许调用方各写一套)。
web 走命令,桌面版在应用内操作(CLI 拒绝 --profile desktop)。
探测按需触发 + 24h 缓存,不做后台定时(探测功能不得变成轮询)。
为什么值得在宿主页面上再贡献一个按钮:宿主插件页能装(npm 包 / Git 仓库 / 本地目录),
但它的「更新」只给官方插件 —— 第三方插件在宿主口径里是「请先卸载再安装新版」
(ui-plugin-manager/src/client/locales.ts 的 installUpgradeNotice / installProblemInstalled)。
⇒ 本插件贡献的徽标+「升级」按钮=把「卸载+重装」换成「原地升级」,这也是一键升级真正的缺口所在。
③ 动作:一键升级 + 守卫
- 走宿主
pluginManager.installBundle:备份 / 回滚 / profile 写锁 / install-script 审批 / 取消
都是宿主现成的,不自己 spawn pnpm。 - 两步确认:点一次只展开说明(在当前 profile 执行 pnpm 安装、改动依赖、装完要重启),
再点确认才真调。这条服务路径绕过了宿主工具层的审批门槛 ⇒ 知情责任落在我方。 - run 进行中拒绝;失败按宿主分型结论渲染(run 中 / 无插件管理服务 / 非 registry / 待构建包是不同的事)。
- 只对 registry 安装开放一键:
file:/ git / tarball 装法一键会把依赖静默换成线上版本
(真机踩过:0.2.7 本地部署被一键切成 registry 版,随后回滚)⇒ 只给可复制的裸命令。
④ 探测底座(host/core/update-check.ts,纯函数)
TEAMFLOW_UPDATE=0可整体关闭(环境开关不进公开 schema——同craft/confirm的处置)。- 「跳过此版本」跳的是具体版本号:更新的版本出现时仍会提示(跳过 ≠ 永久静默)。
- 4s 超时;永不抛:联网失败降级为「不提示」并在返回对象里显式带
error——探测不得成为故障源。 - 版本比较含 prerelease 语义;解析不了的版本一律不提示(宁可漏报不做假提醒)。
⑤ 文档
- README / README.en 新增「升级(对使用者)」:一键与命令两条路径、跳过语义、重启要求。
docs/adr/0012:完整决策记录 + 工具条呈现七条纪律(五轮真机反馈:
整句挤爆工具条 →title点不了 → 浮层够不到(margin 间隙归属)→ 按钮竖排(NO_WRAP)→
复制到说明书(引号括号))。核心教训:一个字符串有两个消费者就是两份契约。
验证
- typecheck 0 错;全量 35 项测试通过。
- 新增
test/update-check.test.js(负向判据为主:不提示的情形比提示的情形多);
四条单维度变异各让对应新断言变红、其余保持绿 ⇒ 断言有牙。 - 真机(desktop profile):探测 → 提示条 / 悬浮层悬停 / 一键复制 / 升级全链路实测通过;
升级后由命令行路径回装验证installCommand的裸命令可直跑。 installRecipe拆分重构前后六个分支逐字节比对(该文案进__runCtx,零漂移)。
English
⬆️ Upgrade notes
- Upgrading from ≤ 0.2.7: this is the version that starts telling you about newer ones — this
particular upgrade is still manual (commands in README "Upgrade (for users)"); after it, new
versions surface in the workspace / plugins page with one-click upgrade. - Restart
dsh --profile webafterwards (or restart the desktop app).
① Why (ADR-0012)
Real-world observation: many users stay on old versions despite frequent releases. Split into two
axes, the problem is NOT "can't install" but ①discovery (users don't know) and ②action cost
(upgrading isn't one click). Reachability (mirror lag etc.) is explicitly out of scope.
First contact does not reach existing users — the notice mechanism itself arrives only by
upgrading ⇒ the lever for the installed base is the README / release notes carrying the upgrade
command.
② Discovery: three surfaces, one implementation
| # | Surface | Who it covers |
|---|---|---|
| ① | Host plugins-page badge + action (plugins.detail.badge / .actions slot contributions) |
People managing plugins in settings — the default place to look for updates |
| ② | Workspace top banner | People opening the workspace (probes once on open) |
| ③ | Global panel "Check for updates" button | The only manual entry: ①② eat a 24h cache, so users need a way to ask |
All three share client/update-ui.tsx (single implementation). Probing is on-demand + 24h cache;
no background polling.
only the upgrade action differs — a command for web, the in-app page for desktop (the CLI refuses
--profile desktop).
Why contribute a button to the host page at all: that page can install (npm package / Git repo
/ local directory), but its "Update" is offered for official plugins only — for third-party plugins
the host's own wording is "uninstall first, then install the new version"
(ui-plugin-manager/src/client/locales.ts: installUpgradeNotice / installProblemInstalled).
⇒ The badge + upgrade button contributed here turn "uninstall + reinstall" into "upgrade in place",
which is the actual gap one-click upgrade fills.
③ Action: one-click upgrade + guards
- Goes through the host's
pluginManager.installBundle(backup / rollback / profile lock /
install-script approval / cancel are the host's, not reimplemented). - Two-step confirm; refuses mid-run; failures render by the host's typed verdict, not one
generic "upgrade failed". - Registry installs only: for
file:/ git / tarball installs, one click would silently swap
the dependency to the registry version (hit on a real machine, then rolled back) ⇒ those get a
copyable bare command instead.
④ Probe base (host/core/update-check.ts, pure functions)
TEAMFLOW_UPDATE=0disables the whole feature (env-only, not in the public schema).- "Skip this version" skips that exact version; a newer one still notifies.
- 4s timeout; never throws — network failure degrades to "no notice" with an explicit
error. - Unparseable versions never notify (prefer a missed notice over a false one).
⑤ Docs
- README / README.en gain "Upgrade (for users)": one-click and command paths, skip semantics, restart.
docs/adr/0012: full decisions + seven toolbar-presentation rules from five rounds of real-machine
feedback. Core lesson: a string with two consumers is two contracts.
Verification
- typecheck clean; all 35 tests pass.
- New
test/update-check.test.js(negative criteria first); four single-dimension mutations each
turned exactly the matching new assertions red ⇒ the assertions have teeth. - Real machine (desktop profile): probe → banner / hover popover / copy / upgrade verified end to end.
installRecipeoutput compared byte-for-byte across six branches before/after the refactor
(that text goes into__runCtx— zero drift).