Files
jkooe e23788d717 refactor(versioning): 统一版本号 —— semver 为唯一版本号,开发批次代号并入其归属版本
## 问题(皇上指出)
项目里两套编号并行,无法判断哪个是"当前版本":
- **发布版本**(semver):v1.1.0 ~ v1.6.0、v2.0.0
- **开发批次代号**:v1 ~ v26(README 路线表 / 代码注释 / 文档)
时间线证据:v1.1.0 首发(09-29)时批次已推进到 v12.4 —— **二者从来不是一对一**(26 批次只发过 10 版)。

## 规则(新增 docs/VERSIONING.md §0 版本号唯一性规范)
- **唯一版本号 = semver `vX.Y.Z`**,两个来源必须永远一致:git tag 与 package.json(构建期注入 agent)
- **批次代号不是版本号**,不再单独出现;需要区分同版内多批改动时用**功能名**
- 附「批次 → 归属发布版本」映射表(按 tag 时间线与功能内容推定,并注明以 CHANGELOG 为准)

## 统一动作
1. **批次 v23 ~ v26 → v2.0.0**(22 文件 / 53 处)—— 它们确实属于 v2.0.0
2. **臣的批次号 v1.7 / v1.8 → v2.0.0**(27 文件)—— 未单独发版
3. **简写补全三段式**:v1.5 → v1.5.0、v1.6 → v1.6.0
4. **README 路线表**:`**v1**` → `**批次 1**`;表头加「批次 ≠ 版本号」说明并链到 §0;
   补齐批次 16~29 与 **v2.0.0 发布行**;删掉过时的「待办」行(提到的分支早已删除)
5. `docs/VERSIONING.md` 里**故意保留** 3 处批次号 —— 那是规范说明本身

## 保留项(真实发布版本,不动)
v1.1.0 / v1.2.1 / v1.3.0 / v1.4.0~v1.4.3 / v1.5.0 / v1.6.0 / v2.0.0(10 个 tag)

## 验证
- 版本号一致性:package.json = tag = 8 个 workspace = tauri.conf.json = **2.0.0** ✓
- v1.7/v1.8/v23~v26 全仓残留 **3 处**(均在 VERSIONING.md 规范说明中,属预期)
- 7 包构建 · 单测 273 · e2e 30 · ps1-encoding 24 全绿
2026-10-10 14:27:12 +08:00

8.2 KiB
Raw Permalink Blame History

版本与兼容性契约

为什么需要这份文档:agent 与 CLI/MCP 是分开更新的(CLI 随控制端装,agent 推/拉到被控端), 新旧混用是常态而非例外。没有明文契约,升级顺序、什么算破坏性改动、出问题怎么定位全靠猜。

0. 版本号唯一性规范(2026-10-10 确立)

背景:项目早期同时存在两套编号 —— 发布版本(v1.1.0/v2.0.0)与开发批次代号(v1~v26)。 二者从来不是一对一(例:v1.1.0 首发时批次已推进到 v12.4),并列出现时无法判断哪个是"当前版本"。 自 v2.0.0 起统一为下列规则。

唯一版本号

vX.Y.Z(semver)是项目唯一的版本标识,两个来源必须永远一致:

来源 位置 谁在读
git tag v2.0.0 人 / GitHub Release
package.json 的 version 全部 workspace + tauri.conf.json 构建期经 esbuild --define 注入 agent
nodeagent info        # 被控端自报的 version 即来自这里
git tag -l            # 发布历史,唯一权威

开发批次代号(已并入发布版本)

v1~v26 是开发阶段的内部代号(一批相关改动的集合),不是版本号,也不再单独出现:

批次 内容 归属发布版本
v1 ~ v12.4 命令级接管 → 事件订阅 / GUI 宏 / Hub 并发(首发前的全部积累) v1.1.0
v12.2 ~ v15 等待语义 / macOS GUI 对齐 / 常驻助手 / 图像模板 v1.2.1 ~ v1.4.x
v16 ~ v18 网络两阶段提交 / 音频 / 构建指纹 v1.4.x
v19 ~ v22 拉取式自更新 / 证书钉住 / 每客户端 PSK / 发现最小化 v1.5.0 ~ v1.6.0
v23 ~ v26、语义属性化、状态采样 连接层三防护 / 零信任安装 / 审计锚定 / UIA 属性 / monitor v2.0.0

上表按 tag 时间线与功能内容推定,近似而非精确(批次与发布本无严格边界)。 判断"某个改动属于哪个版本"时,以 CHANGELOG 的版本段落为准,不以批次号为准。

书写规则(新代码 / 新文档)

场景 写法 例
引用发布版本 vX.Y.Z v2.0.0 新增 gui.await
需要区分同一版本内的多批改动 用功能名,不用批次号 审计锚定(v2.0.0)
历史注释中的批次号 保留但不得再新增;见上表归属 v16:网络两阶段提交

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. 兼容性契约(硬规则)

  1. 只增不改:既有能力的参数名与语义不得变更;要改就新增能力(如 screen.find 与未来的 screen.find2 并存一代),旧能力至少在下一个 MAJOR 才移除。
  2. 不新增必填参数:既有能力只能加可选参数 —— 否则旧控制端一调就 E_PARAM_INVALID。
  3. 返回结构只加不减:新增字段必须可选,旧控制端忽略即可。
  4. auth_ok 的字段只增不减:v20 新增的 agent_version/build/protocol 均为可选, 旧控制端不认也不报错。
  5. 协议主版本(PROTOCOL_VERSION 的主号)不一致时被控端直接拒绝连接并返回 E_PROTOCOL_MISMATCH —— 这是唯一的硬闸门,用于承载无法兼容的协议变更。
  6. 能力清单是唯一事实源:控制端不比版本号,而是比能力集合 (capabilityDiff)—— 版本号可能相同而内容不同(本地 dev 构建),能力集合不会骗人。

3.1 v21 新增字段(均为可选,向后兼容)

位置 字段 含义
被控端 agent.json allow_from: string[] 来源网段白名单;未配置 = 放行全部(与旧行为一致)
被控端 system.info build.cert_sha256 / network.allow_from 证书指纹与白名单状态(供控制端展示与巡检)
被控端 auth_ok build.cert_sha256 同上(握手期即可得)
控制端 nodes.<名> cert_sha256: string 钉住的被控端证书指纹;不一致即 E_CERT_MISMATCH 拒连
发现报文 cert_sha256 未认证广播,仅作参考,不可当身份凭据

新增错误码:E_CERT_MISMATCH (-32422)、E_NET_DENY 类事件走审计 net.deny。

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 的「杀软误拦」)