docs: README/ROADMAP 同步至 v26(能力 51 / MCP 41 / 单测 269 / Rust 9)

README:
- 能力总览补三行(GUI 等待 / 状态采样 / 审计锚定)
- 数字行 43→51 项能力、38→41 个 MCP 工具、215→269 单测、11→9 Rust 单测(实测)
- gui.await 章节补「属性谓词与组合」(where 四属性表 + any_of 示例 + 引擎限制说明)
- 新增两章:状态采样(log.query + monitor)、审计链外部锚定

ROADMAP:
- 头部基线刷新(269 单测 / 51 能力 / 未发版提示)
- §九 台账补 10-09~10-10 的语义 / 状态 / 安全三方向收尾(含 SECURITY §5 清单全部完成)
- 新增「真机验证抓到的 8 个真 bug」表,便于后续复盘
This commit is contained in:
jkooe committed 2026-10-10 00:29:39 +08:00
1 parent 32e0dd7bd5
commit dfd3b2349e
2 files changed
+100 -4

No files matched your search

+67 -1
View File
@@ -30,6 +30,9 @@ nodeagent 把一台 Windows 机器的能力**标准化成一组可授权、可
| **输入** | `input.mouse.move` · `input.mouse.click` · `input.mouse.scroll` · `input.mouse.drag` · `input.key.type` · `input.key.press` | 键鼠控制(🔒 **默认禁用**),含**拖拽**、**字符串热键 / 预设 / 长按 / 后台定向投递** |
| **剪贴板** | `clip.get` · `clip.set` | 读写文本**或图片**(PNG Base64) |
| **事件订阅** | `event.watch` · `event.unwatch` · `event.list` · `event.poll` | 文件变动 / 进程启停 / 端口开闭,**主动推送**(无需轮询) |
| **GUI 等待** | `gui.await` | 等条件成立再返回(窗口/元素/进程/文件 × 出现/消失);支持**属性谓词 `where`** 与 **`any_of` 组合** |
| **状态采样** | `log.query` · `monitor.start` · `monitor.report` · `monitor.stop` · `monitor.list` · `monitor.delete` | 日志**在被控端侧过滤**(不上传整份)/ 定时采样落盘 + **摘要回看**(断几次、最长断多久、CPU/内存值域) |
| **审计锚定** | `system.audit.head` · `system.audit.anchor` | 链头可读 + 锚点**追加**落盘,让「有 root 者整链重写」可被发现 |
## 快捷键(hotkey)控制 —— v1.5
@@ -102,7 +105,70 @@ nodeagent --node win invoke gui.await --args '{"condition":"file","path":"C:\\lo
**典型闭环**:`await control(text:"立即安装")` → `mouse.click(x, y)` → `await control(text:"完成", timeout_ms:60000)` → `click` → `await absent(text:"安装中")`。
**43 项能力** · **38 个 MCP 工具** · **215 项单元测试** · **30 项端到端用例** · **11 项 Rust 单测**(CI 在真实 Windows / Linux runner 上验证)
### 属性谓词与组合(v1.7)
`screen.find` 现在返回 UIA 属性,`gui.await` 可据此等**状态**而不只是「出现」:
```bash
# 等一个「可用」的按钮(不指定文字)
nodeagent --node win await --condition control --where '{"enabled":true}'
# 等值里含「已」的元素
nodeagent --node win await --condition control --where '{"value":"*已*"}' --timeout 15000
# 组合:装成功 或 报错,先出现的算
nodeagent --node win await --any_of '[{"condition":"window","title":"安装完成"},{"condition":"control","text":"错误"}]'
```
| 属性 | 来源 | 拿不到时 |
|---|---|---|
| `enabled` | `IsEnabled` | — |
| `value` | `ValuePattern` | `null`(**不猜**) |
| `selected` | `SelectionItemPattern` | `null` |
| `toggle` | `TogglePattern` | `null` |
> 属性只在 **UIA 引擎**下可读(Windows)。走 OCR/图像模板时 `where` 被忽略并**带回 `note`**,
> 不会让调用方误以为「按属性过滤过了」。macOS 无 UIA 等价物,`where` 不可用(会明确报错提示用 `text`)。
---
## 状态采样:日志过滤与定时采样 —— v1.8
治**间歇性**问题(代理不通、杀软拦截、端口时开时闭)—— `system.status` 只能看当下快照,
而 `event.watch` 只解决「有事件时通知」,没有「**持续记录并回看**」。
```bash
# 日志在被控端侧过滤,只回匹配行(几万行日志也不整份拉回)
nodeagent --node win log --path "C:\app\app.log" --level ERROR --limit 20
nodeagent --node win log --path "C:\app\app.log" --pattern "timeout|refused" --tail
# 连续采样 5 分钟,事后回看「断过几次、最长断多久」
nodeagent --node win invoke monitor.start --args '{"source":"port","target":"127.0.0.1:8765","interval_ms":2000,"id":"probe"}'
nodeagent --node win monitor report probe
nodeagent --node win monitor stop probe
```
采样源四类:`port`(连通性 + 延迟)/ `process`(存活 + PID)/ `command`(退出码 + 首行输出)/
`metric`(CPU·内存)。样本落 `<数据目录>/monitors/<id>.jsonl`,**摘要直接给结论**。
---
## 审计链外部锚定 —— v25
链内哈希只能发现「改一条」。**有 root 的攻击者可以整链重写** —— 从零重建一份自洽的假日志,
`audit.verify` 照样通过。外部锚定是唯一能戳破它的手段:把链头记到**链外**。
```bash
nodeagent --node win audit head --compare # 看链头 + 与最近锚点比对
nodeagent --node win audit anchor --note "每日例行" # 追加一个锚点
```
**锚点记四元组** `{entries, head_hash, head_ts, rotated_segments}`,不能只记哈希 ——
日志轮转会丢弃最旧段使条目数下降,只比哈希会把**轮转误判成篡改**。
锚点文件**只追加**(可覆盖的话,攻击者重写链后再刷新锚点即可抹痕)。
> 真正发挥价值的前提:把锚点文件指向**另一台机器 / 网盘同步目录**。
> 本机文件被整盘重写时,锚点也随之消失 —— 那就等于没锚。
**51 项能力** · **41 个 MCP 工具** · **269 项单元测试** · **30 项端到端用例** · **9 项 Rust 单测**(CI 在真实 Windows / Linux runner 上验证)
> 关键里程碑:**GUI 语义**(`window.list` + `screen.find`,UIA 找不到自动降级 OCR)
> 让 AI 从「看得到画面但读不懂界面」变成「按名字取坐标点下去」。
+33 -3
View File
@@ -1,8 +1,9 @@
# nodeagent 进度盘点与下一步路线
> 盘点日期:2026-10-07(**第三次更新**:v1.6.0 已发、CI 全绿后据实刷新)
> 盘点方式:只读核查仓库(`git fetch` + 源码 grep + 本地跑单测 + `gh run list`)
> 基线:远端 main(tag **v1.6.0** 之后)|单测 **221 全绿** · e2e **30 全绿** · Windows CI **13/13 全绿**
> 盘点日期:2026-10-10(**第四次更新**:语义/状态/安全三个方向全部落地后据实刷新)
> 盘点方式:真机验证 + 本地跑单测 + `gh run list`(Windows 被控端曾于 10-10 00:15 离线)
> 基线:远端 main(`9ad296b` 之后)|单测 **269 全绿** · e2e **30 全绿** · 能力 **51 项** · Windows CI 全绿
> ⚠️ 全部**未发版**(皇上要求先攒着)—— 下次发版建议直接跳 **v2.0.0**
> 适用范围:本文档随仓库公开,**不含任何真机地址与账户信息**(见 §6 脱敏约定)
---
@@ -374,6 +375,35 @@ node scripts/verify.mjs --host <地址> --key <密钥> --report <报告.md>
## 九、进展台账(2026-10-07 第三/四次盘点后完成)
### 10-09 ~ 10-10 追加(语义 / 状态 / 安全三方向收尾)
| 事项 | 状态 | 说明 |
|---|---|---|
| 语义方向:`screen.find` 读 UIA 属性 + `where` 谓词 + `gui.await` 组合 | ✅ | `enabled/value/selected/toggle`;拿不到留 `null` 不猜;`any_of` 组合;能力数 43→49 |
| 状态方向:`log.query` + `monitor.*`(5 项) | ✅ | 日志被控端侧过滤;采样落盘 + 摘要回看(断几次/最长断多久/CPU·内存值域) |
| 安全:连接层三防护(握手封禁 / 连接上限 / 空闲断开) | ✅ | 本地真机三场景验证(正确密钥可连 → 连续错误被拒 → 封禁期正确密钥也被拒) |
| 安全:安装期密钥改 stdin | ✅ | `install.cmd` 管道喂 `-KeyFromStdin`,不再出现在命令行 |
| 安全:`ed25519` 默认化 → **零信任优先安装** | ✅ | 有 `client-acl.json` 即走 ed25519(`default_effect=deny`);否则 psk + 醒目告警 |
| 安全:**审计链外部锚定** | ✅ | `system.audit.head` + `system.audit.anchor`;四元组锚点防轮转误判、追加式防刷新抹痕;能力数 49→51 |
| 自重启在**非管理员**身份下的降级 | ✅ | 真机坐实:`-RunLevel Highest` 被拒 → 回退不指定 Principal;`deploy` 由此首次一次走通 |
| deploy 备份不再堆积 | ✅ | `.bak-<ts>` → 固定名 `.bak` + 清理历史;真机 17 个文件 → 2 个 |
| 自重启任务 Running 僵尸清理 | ✅ 代码 / ⏳ 真机 | 清理条件加「LastRunTime 早于 1 小时」余量(防误杀当前宿主) |
| **`SECURITY.md` §5「还没做的」清单** | ✅ **全部完成** | 见 §4.5~§4.8 |
### 真机验证过程中抓到的真 bug(8 个,全部只有真机能发现)
| # | bug | 性质 |
|---|---|---|
| 1 | `install.cmd` 双击首装即炸(`for /f` 调 PS,`^|` 漏进 PowerShell) | **用户首装必炸** |
| 2 | `pack.mjs` 三连(spawn `.bin/esbuild` ENOENT / `zip` / `ls -la`+`du -sh`) | 打包脚本全平台假设 |
| 3 | `where` 的 PS 注入语法错(连修**两轮**才通:JSON 字面量 ✗ → `\"` 转义 ✗ → PS 单引号 ✓) | 跨语言转义 |
| 4 | `screen.find` 的 where-only 被自己入口校验拦下 | v1.7 只改了一半 |
| 5 | 自重启在非管理员下失败 → `deploy` 永远"半成功" | v7 起的既有缺陷 |
| 6 | `parseLeadingTs` 吃掉 ISO 的 `Z` 时区标记(偏 8 小时) | 单测抓到 |
| 7 | manifest 承诺的 `note` 字段代码没实现 | 承诺未落地 |
| 8 | 锚定动作自身写审计导致"紧随比对恒显示已增长" | 语义瑕疵(已说明) |
| 事项 | 状态 | 说明 |
|---|---|---|
| `gui.await` 收尾(断言/单测/入口/版本号/本地真机/发版) | ✅ | v1.6.0 |