## 一、版本功能(防新旧混用) 1. **单一版本源 + 构建注入**:根 package.json 的 version、git sha、构建时间经 esbuild `--define` 注入 agent;`system.info.build` 新增 `version/commit/built_at`(原有 `hash` 指纹不变)。 开发态用 typeof 探测(未注入也不崩)。 2. **握手携带版本**:`auth_ok` 新增可选字段 `agent_version` / `build` / `protocol`(只增不减 → 旧控制端忽略即可)。 3. **能力级兼容比对**:新增纯函数 `capabilityDiff(local, remote)` —— - `missingOnRemote`:本地有远端没有 → **远端较旧**,提示更新被控端 - `unknownLocally`:远端有本地没有 → **本控制端较旧**(不影响既有功能) 控制端不比版本号而比**能力集合**:版本号可能相同而内容不同,能力集合不会骗人。 4. **CLI 体检**:`nodeagent info` / `connect` 直接显示被控端版本与能力对齐情况, 把"调用时才炸"变成一句人话提示。 ## 二、发版自动化(GitHub 资产同步) `pnpm release <x.y.z|patch|minor|major>`:前置检查(干净/在 main/与远端同步)→ 改 7 个 package.json → 构建+打包 → 生成资产 → 提交打标签推送 → `gh release create` 上传。 - 资产:`agent.mjs`(**自更新载荷**)、`nodeagent-win-x64.zip`(人工安装包)、 `latest.json`(version/commit/能力数 + 两个资产的 url 与 sha256)、`SHA256SUMS` - `--dry-run` 无副作用演练;`--no-publish` 只到本地标签 - 能力数从 dist 动态取(避免写死漂移);刻意排除 `.github/`(缺 workflow 权限推不了) ## 三、兼容性契约文档 新增 `docs/VERSIONING.md`:semver 各位的含义、**只增不改**六条硬规则、 升级顺序建议(**先被控端**:新被控端向后兼容旧 CLI)、两条更新路径、发版流程、排查手册。 ## 验证 - 单测 147 → **149**(新增 capabilityDiff 用例:远端过旧 / 本机过旧 / 完全一致 / 真实清单自比无差异) - e2e 28 全绿;`node scripts/release.mjs 1.0.0 --dry-run` 演练通过且工作区无副作用
5.3 KiB
5.3 KiB
版本与兼容性契约
为什么需要这份文档:agent 与 CLI/MCP 是分开更新的(CLI 随控制端装,agent 推/拉到被控端), 新旧混用是常态而非例外。没有明文契约,升级顺序、什么算破坏性改动、出问题怎么定位全靠猜。
1. 版本号
- 单一来源:根
package.json的version,构建时经 esbuild--define注入 agent。 - 运行时可通过
system.info的build字段读到:version/commit(git sha)/built_at/hash(运行中脚本的 sha256 前 12 位 —— 部署校验用)。 - 控制端在握手(
auth_ok)即拿到被控端版本,nodeagent info会显示:被控端版本 : v1.0.0 @ ef9ad97 构建 740d51953fd0 能力对齐 : 42 项,两端一致 ✓
2. 语义化版本的含义
| 变更类型 | 版本位 | 例子 |
|---|---|---|
| 破坏性:删除能力、给既有能力新增必填参数、改变既有能力的返回结构、修改认证/握手流程 | MAJOR | 删掉 screen.find;把 fs.read 的 path 改成必填 remote_path |
| 新增:新能力、既有能力的可选参数、新增返回字段、新增审计事件类型 | MINOR | 加 system.audio.*、给 deploy 加 --check |
| 修复/内部:bug 修复、性能、文档、不影响协议的重构 | PATCH | 修备份编码、拆分文件 |
3. 兼容性契约(硬规则)
- 只增不改:既有能力的参数名与语义不得变更;要改就新增能力(如
screen.find与未来的screen.find2并存一代),旧能力至少在下一个 MAJOR 才移除。 - 不新增必填参数:既有能力只能加可选参数 —— 否则旧控制端一调就
E_PARAM_INVALID。 - 返回结构只加不减:新增字段必须可选,旧控制端忽略即可。
auth_ok的字段只增不减:v20 新增的agent_version/build/protocol均为可选, 旧控制端不认也不报错。- 协议主版本(
PROTOCOL_VERSION的主号)不一致时被控端直接拒绝连接并返回E_PROTOCOL_MISMATCH—— 这是唯一的硬闸门,用于承载无法兼容的协议变更。 - 能力清单是唯一事实源:控制端不比版本号,而是比能力集合
(
capabilityDiff)—— 版本号可能相同而内容不同(本地 dev 构建),能力集合不会骗人。
4. 升级顺序建议
先升级被控端,再升级控制端:
| 组合 | 结果 |
|---|---|
| 新被控端 + 旧控制端 | ✅ 安全。被控端只做加法,旧控制端不认识的新能力会被忽略 |
| 旧被控端 + 新控制端 | ⚠️ 可用,但新功能不可用。nodeagent info 会列出远端缺少的能力并提示升级 |
若顺序反了也不会坏:控制端调用缺失能力时返回
E_CAPABILITY_NOT_FOUND, 而nodeagent info/connect会提前把它变成一句人话提示。
5. 更新方式(两条路,按可达性选)
| 方式 | 命令 | 前提 |
|---|---|---|
| 推送式 | pnpm update:win <设备名> |
控制端↔被控端可达;自动打包含校验与指纹复核 |
| 拉取式 | nodeagent update --url <地址> --sha256 <哈希> |
只要被控端能上网(含 GitHub Release) |
先预检再更新(不动任何文件):
nodeagent --node win update --url <地址> --sha256 <哈希> --dry-run
--sha256 是安全底线:不校验哈希等于开放远程代码执行。
6. 发版流程
pnpm release patch|minor|major|<x.y.z> # 改版本 → 构建 → 打包 → 校验和/清单 → 提交打标签 → GitHub Release
pnpm release 1.0.0 --dry-run # 演练(无任何副作用)
产物(GitHub Release 资产):
| 资产 | 用途 |
|---|---|
agent.mjs |
自更新载荷 —— 被控端 system.agent.update 直接用它(配 --sha256) |
nodeagent-win-x64.zip |
人工安装包(自包含 node.exe,免装 Node.js) |
latest.json |
版本清单:version/commit/built_at/capabilities + 两个资产的 url 与 sha256 |
SHA256SUMS |
校验和 |
⚠️ 首次仍需一次性安装:自更新能力本身得先在机器上(鸡生蛋无解)。 ⚠️
node.exe(Node 运行时)不在自更新范围:只换agent.mjs;需升级 Node 时重跑install.ps1。 ⚠️ 本仓库的.github/改动需要workflow权限才能推送(gh auth refresh -h github.com -s workflow), 发版脚本刻意跳过.github/,避免发版流程被它卡住。
7. 出问题怎么排查
nodeagent --node win info # 版本 + 能力对齐情况(先看这个)
nodeagent --node win invoke system.info --args '{"fields":["build"]}' # 构建指纹
nodeagent --node win deploy <包> --check # 本地包 vs 远端是否一致(不改动)
- 远端过旧:
info会列出缺失能力 →nodeagent update ... - 本控制端过旧:
info会列出"不认识的新能力" → 更新控制端(git pull→pack:mac→install-macos.sh) - 同版本但指纹不同:属正常(内容相同才叫同版本);用
--check对比实际内容 - 部署后没生效:先查杀软是否处置了被替换的
agent.mjs(见REMOTE-LIMITS.md的「杀软误拦」)