2 Commits
Author SHA1 Message Date
jkooe 3ff8e86fe8 chore(release): v1.4.3 2026-10-05 11:35:53 +08:00
jkooe 7689a1f80c fix(v22): 构建改为**确定性** —— 去掉注入的构建时间,否则「指纹比对是否最新」永远失真
## 现象(真机执行 `deploy --check`)
明明被控端就跑着 v1.4.2、本地也是 v1.4.2,却报「版本不一致」:
    本地 : 47326da097c7  (976 KB)
    远端 : 6499b3f7ad3c  (node v22.20.0)

## 根因
打包时注入了 `__BUILD_TIME__`,于是**同一份代码每次打包的字节都不同** →
内容哈希无法充当「构建身份」,`--check` / 部署后校验只能靠"这次上传的文件"比对,
跨批次就必然误报。

## 修复
- `pack.mjs` 不再注入构建时间(版本与 commit 仍是确定性的)
- `system.info.build.built_at` 改用**入口文件的 mtime**(mtime 不在文件内容里,
  因此不破坏字节确定性,同时仍能反映构建时刻)

## 验证
- 连打两次包(间隔 2s),指纹完全相同:`b13ce7d3ba74d0e0b293649f` ✓
- 真机更新:指纹 b13ce7d3ba74、42 项能力、证书钉住一致 ✓
- **再次执行 `--check` → 「✓ 已是最新,无需更新」**(此前同版本却报不一致)✓

## 意义
产物变为**可复现**(reproducible):同一 commit 打出同一份字节,
sha256 可以放心用作分发与更新的身份标识 —— 这对一条「下载即执行代码」的更新通道很关键。
2026-10-05 11:27:28 +08:00
9 changed files with 15 additions and 13 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nodeagent/agent",
"version": "1.4.2",
"version": "1.4.3",
"private": true,
"type": "module",
"main": "./dist/index.js",
+5 -5
View File
@@ -55,7 +55,6 @@ async function isAdmin(): Promise<boolean> {
// 故一律用 typeof 探测,绝不能直接引用(否则 dev 启动就 ReferenceError)。
declare const __AGENT_VERSION__: string | undefined;
declare const __BUILD_COMMIT__: string | undefined;
declare const __BUILD_TIME__: string | undefined;
/** v21:读取当前生效的来源白名单(用于 system.info 上报)。 */
function configAllowFrom(): string[] | null {
@@ -67,10 +66,9 @@ function configAllowFrom(): string[] | null {
}
}
function injected(name: 'version' | 'commit' | 'time'): string {
function injected(name: 'version' | 'commit'): string {
if (name === 'version') return typeof __AGENT_VERSION__ === 'undefined' ? 'dev' : __AGENT_VERSION__;
if (name === 'commit') return typeof __BUILD_COMMIT__ === 'undefined' ? 'dev' : __BUILD_COMMIT__;
return typeof __BUILD_TIME__ === 'undefined' ? 'dev' : __BUILD_TIME__;
return typeof __BUILD_COMMIT__ === 'undefined' ? 'dev' : __BUILD_COMMIT__;
}
const STARTED_AT = Date.now();
@@ -118,7 +116,9 @@ export function buildInfo(): {
// v20:语义化版本 + 提交 + 构建时间(来自构建期注入)
version: injected('version'),
commit: injected('commit'),
built_at: injected('time'),
// 构建时间取入口文件的 mtime(而非构建期注入)—— 注入会让每次打包字节不同,
// 破坏「指纹 = 构建身份」的确定性(v22 真机踩过)
built_at: buildCache.mtime_ms ? new Date(buildCache.mtime_ms).toISOString() : 'unknown',
// v21:TLS 证书指纹(控制端据此钉住被控端身份)
cert_sha256: currentCertFingerprint(),
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nodeagent/cli",
"version": "1.4.2",
"version": "1.4.3",
"private": true,
"type": "module",
"main": "./dist/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nodeagent/hub",
"version": "1.4.2",
"version": "1.4.3",
"private": true,
"type": "module",
"main": "./dist/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nodeagent/mcp",
"version": "1.4.2",
"version": "1.4.3",
"private": true,
"type": "module",
"main": "./dist/index.js",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "nodeagent",
"private": true,
"version": "1.4.2",
"version": "1.4.3",
"description": "跨机 AI 接管框架 —— 让 Mac 上的 AI 无缝接管 Windows,装软件、查状态",
"type": "module",
"engines": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nodeagent/client",
"version": "1.4.2",
"version": "1.4.3",
"private": true,
"type": "module",
"main": "./dist/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nodeagent/protocol",
"version": "1.4.2",
"version": "1.4.3",
"private": true,
"type": "module",
"main": "./dist/index.js",
+3 -1
View File
@@ -97,11 +97,13 @@ async function main() {
'--target=node22',
'--format=esm',
'--banner:js=import { createRequire } from "node:module"; const require = createRequire(import.meta.url);',
// 刻意**不注入构建时间**:注入后同一份代码每次打包字节都不同,
// 「指纹比对是否最新」就永远失真(真机踩过:明明同版本却报不一致)。
// 构建时间改由入口文件的 mtime 反映(见 system.ts 的 build.built_at)。
// 注意:define 的值按 **JS 表达式** 解析,故用双引号包裹;写成单引号会让引号成为值的一部分
// (真机踩过:版本显示成 v"1.4.0")。
`--define:__AGENT_VERSION__=${JSON.stringify(pkg.version)}`,
`--define:__BUILD_COMMIT__=${JSON.stringify(commit)}`,
`--define:__BUILD_TIME__=${JSON.stringify(builtAt)}`,
`--outfile=${bundleOut}`,
],
{ stdio: 'inherit' },