Skip to content

v0.2.8

Choose a tag to compare

@MichaelShii MichaelShii released this 11 Oct 19:12
· 85 commits to main since this release

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 与桌面版都可见(「插件」页是两端同一个页面);差别只在升级动作归哪个 profile:
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 web afterwards (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.

⚠️ All three are visible on both web and desktop (the "Plugins" page is the same page on both);
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=0 disables 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.
  • installRecipe output compared byte-for-byte across six branches before/after the refactor
    (that text goes into __runCtx — zero drift).