jkooe
|
cb468df7da
|
feat(agent): 看门狗自动恢复 LKG —— 启动连续失败 N 次从 LKG 恢复(决策 R-1)
scripts/startup-guard.mjs + scripts/lib/startup-guard-core.mjs(新增):
- 被控端启动连续失败 N 次(默认 3)自动从 LKG 指纹匹配的备份恢复入口脚本
- 30s 启动宽限期 + 端口探活,避免计划内重启与 supervisor 重启撞车被误计数
- 无 LKG / 无指纹匹配只记日志放行,绝不擦除 install-dir
- 恢复成功或服务已就绪均归零失败计数;被控端启动成功后 resetStartupGuardState 归零
(apps/agent/src/index.ts 为本任务对 agent 的唯一改动点)
三端 supervisor 接入:
- Windows install.ps1:计划任务包一层 guard,RestartCount 3→10(保证阈值可达)
- Linux install-linux.sh:systemd unit 的 ExecStart 包一层 guard
- macOS launchd:无安装器,docs/DEVELOPMENT.md §7.4 给出完整接法
docs:DELIVERY-PIPELINE §6 由「不做自动回滚」收窄为
「手动 rollback + 看门狗自动恢复 + 边界明示」,附录 C 同步;
DEVELOPMENT.md 新增 §7.4 三端 supervisor 接入说明。
|
2026-10-26 00:09:56 +08:00 |
|
jkooe
|
b9236dfaf1
|
chore(privacy): 公开仓库隐私脱敏 —— 清除可定位到个人与设备的字面量
按 security-reports/ 的隐私审查台账逐项收口。纯脱敏与表述泛化,
不含任何业务逻辑变更;实测结论与排查步骤一字未动。
P1(可定位到具体设备/个人)
- scripts/verify-b2.mjs:NA_URL 去掉硬编码缺省值(原指向一台真实机器),改为必填。
这是全仓库唯一「拿着就能连到某台真机」的常量。
- 测试账户名 pdverify → na-test(docs/VM-VERIFICATION.md 7 处、docs/console-plan.md 1 处)
- 虚拟机与宿主的真实网段 → RFC 5737 文档保留段 192.0.2.x(含 CLOSE_WAIT 记录)
- 跨项目名与私人称呼 → 中性表述(docs/PRD.md、docs/DEVELOPMENT.md、docs/console-plan.md 等)
P2(低风险设备指纹)
- 虚拟化产品及其 CLI / 访客工具名 → 中性表述(12 个文件 + 1 个工作流注释)
- 测试目录 C:\nodeagent-test → C:\na-test
- NAS 设备型号与系统名 → NAS 设备 / NAS 系统(docs/DEPLOY-LINUX.md、scripts/install-linux.sh、README.md)
- 真机 CPU 型号、真机物理分辨率、代理软件品牌与监听端口、杀软品牌 → 泛化
- 单元测试示例段去掉真机 IP 尾八位:acl-v11 / net-scripts 的 fixture 与断言同步更新
- .gitignore 补 config.json / agent.json(含真实密钥与节点地址,绝不入库)
- 顺带移除混入技术文档的「不构成投资建议」字样(与文档主题无关)
验证:pnpm test:unit 340/340 通过;独立扫描器对 235 个跟踪文件做 17 类模式
全量扫描,命中 0。
|
2026-10-13 22:35:40 +08:00 |
|
jkooe
|
a0be43d76c
|
refactor(pack): 被控端 esbuild 配置抽为单一实现
`agent.mjs` 现在有两个消费方:
1. scripts/pack.mjs → Windows 便携包 / 自更新载荷(Release 资产)
2. scripts/prepare-console.mjs → 桌面控制台的随包资源(控制台要能托起本机被控端)
若各写一份 esbuild 配置就是「两套真相」——改了一处漏另一处时构建过、单测过,
只在真机产物里暴露(本项目已多次吃过这类亏)。故配置只此一份。
bundle-agent.mjs 导出 repoRoot / repoVersion / gitShortCommit / bundleAgent,
沿用原有全部约束:JS API(pnpm 隔离布局下 .bin/esbuild 在 Windows 不存在)、
不注入构建时间(否则每次打包字节不同,指纹比对失真)、
define 用 JSON.stringify 得纯双引号。
本次对 pack.mjs 的输出无语义变化(同一份 options 原样迁移)。
|
2026-10-11 11:41:38 +08:00 |
|
jkooe
|
5e6231493e
|
fix(pack): Windows 打包条目带目录前缀 —— 与 macOS 产物结构不一致
CI 在 f85acad 上红了:`✗ zip 内未找到条目 node.exe: release/nodeagent-win-x64.zip`
(而本地 macOS 全绿 —— 方向恰好相反)。根因是两平台用了不同打包器:
- macOS/Linux: `cd <dir> && zip -r <out> .` → 条目是裸名 `node.exe`
- Windows : `Compress-Archive -Path '<dir>\*'` → 条目带前缀 `<dir>/node.exe`
两处一起修:
1. `scripts/lib/zip-pe-arch.mjs`:条目名匹配改用 **basename**,兼容两种打包方式。
已用构造的带前缀 zip 实测通过,并注入错架构验证守卫仍会红。
2. `scripts/pack.mjs`:Windows 分支改用 .NET `ZipFile::CreateFromDirectory`
—— 它打包**目录内容**,条目同样是裸名,从根上消除两平台产物结构差异。
否则 CI 校验的产物与发布产物**不同构**,校验本身的价值打折;
且「解压后双击 install.cmd」的说明会因多一层目录而失配。
教训:**「本地全绿」≠「Windows 绿」** —— 平台差异只有真 runner 能暴露(第 N 次实证)。
|
2026-10-11 10:32:41 +08:00 |
|
jkooe
|
b2404291b0
|
fix(pack): arm64 包内实为 x64 node —— 缓存名不含架构 + 补 PE 架构断言
release.mjs 先打 x64 时会把运行时缓存为 node-runtime.zip(该名不含架构),
随后打 arm64 时误判「已缓存」直接复用 → arm64 包内 node.exe 实为 x86-64
(两份公开包内 node.exe 字节完全相同,sha256 同为 fdddbf45…,PE Machine=0x8664)。
该包在 ARM Windows 上能启动、能 listen,但 TLS 握手不完成(控制端只见 E_TIMEOUT),
且 macOS 侧完全复现不出。
- pack.mjs: 缓存名改为 node-runtime-<arch>.zip(按架构隔离)
- pack.mjs: 解包后断言 node.exe 的 PE Machine 与 --arch 一致(防呆 C,已注入故障实测会红)
- ci: windows-e2e 补「双架构包 PE 架构校验」,直读最终 zip 内 node.exe 的 PE 头
(此前 CI 只打 x64 且只验 x64,arm64 打包零覆盖 —— 该 bug 必然逃过 CI)
- docs: CHANGELOG 记录 ARM64 真机对照实测
真机实测(ARM64 Windows 虚拟机,同机同参对照):
arm64 → TLS 握手成功 + PSK + 51 项能力两端一致 + exec/审计全通
x64 → E_TIMEOUT(复现「能 listen 但 TLS 握手不完成」)
|
2026-10-11 09:42:32 +08:00 |
|
jkooe
|
d8f21f4e68
|
feat(pack): 支持 --arch(x64 / arm64)—— ARM Windows 上 x64 包不可用(真机实证)
## 背景(Parallels Win11 ARM 真机验证的结论)
x64 包(内含 x64 Node)在 ARM Windows 上:**能启动、能 listen 8765,但 TLS 握手 60s 不完成**
(Prism 模拟下协议层异常)→ 协议层完全不可用,且现象具有误导性(端口是通的)。
换同版本 ARM64 Node 后**完全正常**(握手成功、连接、51 项能力)。
## 改动
`scripts/pack.mjs` 新增 `--arch <x64|arm64>`(默认 x64,向后兼容):
- Node 下载 URL、包目录、产物名全部按架构生成
- 产出:`nodeagent-win-arm64.zip`(ARM Windows 用)/ `nodeagent-win-x64.zip`(默认)
- 加了架构校验(非法值直接报错)
## 验证
- `node --check` 语法通过
- 实跑 `node scripts/pack.mjs --arch arm64` → 产出 31.4 MB 包,
内含 ARM64 `node.exe`(85.6 MB 解压后)+ `agent.mjs`
|
2026-10-11 00:30:43 +08:00 |
|
jkooe
|
5b69d6cec9
|
refactor: 项目结构与命名规范化(重写前的最后一次常规提交)
## 改名
`scripts/run-temp.cmd` → `scripts/run-portable.cmd`
理由:它是**正式功能**(免安装、免管理员的便携启动方案),"temp" 易被误认成临时文件。
程序内文案同步:TEMPORARY launcher → PORTABLE launcher;
打包产物 README 的"【临时试用,不装也行】" → "【免安装试用,不装也行】"。
## 移动
`PRD.md` → `docs/PRD.md`
理由:文档统一放 docs/(README/CHANGELOG 按惯例留在根)。
## 同步的引用(共 11 处)
- scripts/pack.mjs:产物文件名校验 + 生成 README 的 3 处指引文案
- scripts/run-portable.cmd:自身用法注释 6 处
- README.md:目录树 + 文档链接
- docs/DEVELOPMENT.md:配套文档链接(`../PRD.md` → `PRD.md`,现已同目录)+ 目录树
## 验证
- 全仓 `run-temp` 残留 **0**;`./PRD.md`/`../PRD.md` 旧路径残留 **0**
- 7 包构建通过;单测 273 全绿;ps1-encoding 24 全绿
- `pack.mjs` 实跑通过,`run-portable.cmd` 已正确进入 Windows 产物
- `.cmd` 保持 ASCII + CRLF(94 行);PRD.md 无内部相对链接需调整
|
2026-10-10 13:39:42 +08:00 |
|
jkooe
|
933863529a
|
docs: 包内 README 密钥说明与实际一致
README 原写「PSK.txt 里是预共享密钥」,但 PSK.txt 自 045e3b0 起已不再随包
分发(该 zip 会作为公开 Release 资产,打进密钥等于公开)。说明与事实不符,
首次上手者会找不到这个文件。
改为按实际取值顺序描述:① 本目录已有 PSK.txt 则用之
② 已有 %USERPROFILE%\.nodeagent\agent.json 则复用其中 key
③ 都没有则自动生成随机密钥;并指明密钥会打印在屏幕上、
可用 run-temp.cmd status 查看。
|
2026-10-09 23:15:56 +08:00 |
|
jkooe
|
4cb8ad566b
|
feat: run-temp.cmd —— 免安装、免管理员的临时启动方案
问题:Windows 端 agent 离线时,control.cmd 依赖已安装的计划任务
(Start-ScheduledTask),未安装就完全起不来;而 install.cmd 需要管理员
权限并写防火墙规则,临时试用太重。
run-temp.cmd 直接 node.exe agent.mjs —— agent 本身零配置,
首次运行会自动生成 %USERPROFILE%\.nodeagent\agent.json(随机 PSK +
自签证书)并开始监听,故无需任何前置步骤。
用法:
run-temp.cmd 前台启动(Ctrl+C 停)
run-temp.cmd background 最小化后台启动
run-temp.cmd status 查看进程 / 监听 / 配置与密钥
run-temp.cmd stop 结束
实现要点:
- agent **不解析命令行参数**(argv 出现 0 次),端口只来自 agent.json
→ 脚本不再传 --port(传了也会被忽略,属误导),脚本内 PORT 仅用于状态检查
- CMD 批处理规避:start "title" 后必须套 cmd /c ""..."" 才不吞标题;
子程序用 goto 而非 call(call :LABEL 会在末尾 exit /b 时连带退出)
- 进程检测用 Win32_Process 查 CommandLine,而非 Get-Process.Path
(后者在非管理员下拿不到 Path 属性)
pack.mjs 同步:
- 把 run-temp.cmd 纳入文件清单
- 新增 CRLF 守卫(此前只查非 ASCII)→ LF-only 的 .cmd 在部分
Windows 环境会解析异常
- 两个守卫均已注入故障实测会红(LF-only 94 处 / 非 ASCII 偏移 1677)
- 包内 README 增补临时试用说明
|
2026-10-09 23:12:14 +08:00 |
|
jkooe
|
9efd290061
|
fix(pack): 打 zip 改为平台分流(Windows 无 Info-ZIP),顺带修 ls/du
Windows CI 连续暴露的第三类问题:
- zip -q -r ... -x PSK.txt → Windows runner 没有 Info-ZIP,命令不存在
- 同文件里还有 ls -la 与 du -sh,在 Windows 上同样不可用
修法:
- zip:Windows 用 PowerShell 的 Compress-Archive(先删 PSK.txt 再压,因为它没有 -x 排除项);
macOS 继续用 zip
- 列目录改用 Node readdirSync+statSync;体积用 statSync 计算(不再依赖 du)
macOS 路径复跑通过(zip 31M),195 单测 / 29 e2e 绿。
|
2026-10-07 02:41:18 +08:00 |
|
jkooe
|
4140b8e32c
|
fix(pack): 改用 esbuild 的 JS API,不再 spawn node_modules/.bin/esbuild
Windows CI 的产物包步骤暴露:pnpm 的隔离布局下那个 .bin shim 在 Windows 上不存在 →
spawnSync D:\a\nodeagent\node_modules\.bin\esbuild ENOENT
(macOS 上一直正常,所以此前从未发现。)
改用 await import('esbuild') + esbuild.build({...}) 后只依赖 require 解析,跨平台一致。
本地复跑 pack:win 通过(bundle 1010.8kb / zip 32M),195 单测 / 29 e2e 复跑全绿。
|
2026-10-07 02:35:23 +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 |
|
jkooe
|
f88738d36d
|
fix(v22): 修正构建期注入的版本号带多余引号(真机暴露)
## 现象
真机 `nodeagent info` 显示 `被控端版本 : v"1.4.0" @ "e841fbb"` —— 值里带了引号。
## 根因
esbuild 的 `--define:K=V` 中 **V 是按 JS 表达式解析的**。原先写成
`--define:__AGENT_VERSION__='"1.4.0"'`(单引号包裹),单引号被当成**字符串内容**,
于是运行时值变成 `"1.4.0"`(含引号)。产物内联可见:
`return false ? "dev" : '"e841fbb"';`
## 修复
改用 `JSON.stringify(v)`(纯双引号)→ 产物内联为 `return false ? "dev" : "1.4.0";` ✓
真机复核:`被控端版本 : v1.4.0 @ 50ac4b9 构建 05abbbc79abf` ✓
## 附带真机确认(v1.4.0 部署)
- 推送式部署 + **指纹校验通过**(能力 42 项、PID 16664)
- **证书指纹 TOFU 钉住生效**,且普通 agent 更新**不影响**钉住(证书未变 → 仍一致 ✓)——
这正是设计意图:只有证书重建才会触发 E_CERT_MISMATCH
- **Windows 音频能力真机跑通**(Core Audio COM 路径):`✓ 已应用:🔇 静音 · 音量 77%`,
且能**读回真实状态**(确定性设置而非切换)—— 这补上了此前只在 macOS 验证的空缺
|
2026-10-05 02:45:51 +08:00 |
|
jkooe
|
045e3b08b1
|
fix(sec): 安装包不再打包 PSK.txt(该 zip 会作为公开 Release 资产发布)
## 问题
`nodeagent-win-x64.zip` 里含 `PSK.txt`(64 位预共享密钥)。而这个 zip 正是 GitHub
Release 的公开资产 —— 等于**把被控端的预共享密钥公开**。若谁用该 Release 包安装,
其密钥即为公开值,同网段的任何人都能凭此接入。
## 修复
- `pack.mjs` 打 zip 时用 `-x "PSK.txt"` 排除(并注明原因,防日后被加回)
- 不影响安装:`install.cmd` 在 PSK.txt 缺失时**本地自动生成**密钥(只留在被控端安装目录),
这正是它头注释里既有的行为
- 新增单测守卫:断言打 zip 命令必须带 `-x PSK.txt`(防止回归)
## 兼容性
PATCH 级:仅打包内容变化,不涉及协议/能力/参数。已安装的机器不受影响。
|
2026-10-05 00:25:44 +08:00 |
|
jkooe
|
cedc46d090
|
feat(v20): 版本纪律 + 发版自动化(对齐「做好版本功能 + GitHub 同步资产」)
## 一、版本功能(防新旧混用)
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` 演练通过且工作区无副作用
|
2026-10-04 23:54:51 +08:00 |
|
jkooe
|
4c9b44288c
|
fix(pack): esbuild 输入改用绝对路径,支持从任意目录调用
此前 pack.mjs 用相对路径 apps/agent/dist/index.js 作为 esbuild 输入,
从项目目录外调用(node <abs>/scripts/pack.mjs)时报
Could not resolve apps/agent/dist/index.js。
改为基于 __dirname 的绝对路径;README 补充两种调用方式并注明需在项目目录执行。
验证:cd /tmp && node <abs>/scripts/pack.mjs 可正常产出 32MB 安装包
|
2026-09-27 19:21:21 +08:00 |
|
jkooe
|
192e08b171
|
feat: Windows 便携安装包构建(免装 Node.js)
新增 pnpm pack:win(scripts/pack.mjs),一键产出 nodeagent-win-x64.zip:
- esbuild 打包被控端为单文件 bundle(含全部依赖,约 800KB)
- 自动下载 Node.js Windows x64 运行时(npmmirror,node.exe)
- 组装便携包并打 zip(node.exe + agent.mjs + install.ps1 + uninstall.ps1)
改造 install.ps1:
- 优先使用包内 node.exe + agent.mjs(便携包模式)
- 否则回退系统 Node.js + 仓库构建产物(开发模式)
- 无 Node.js 的 Windows 机器也能一键安装
配套:
- .gitignore 忽略 release/(产物体积大,不入库)
- README 安装说明改为「安装包(推荐)/ 开发模式」两方式
验证:打包产物 32MB;解压后 agent.mjs 可独立运行(19 项能力、wss 监听);
esbuild bundle 通过 banner 提供 createRequire 兼容 CJS 依赖(node-forge)
|
2026-09-27 18:03:04 +08:00 |
|