jkooe
|
591e44721a
|
style(console): 界面一律不使用图标 —— 导航改纯文字,并关闭内置插画/图标
触发原因是一次真实的渲染事故:侧栏那些 emoji 图标在非系统字体栈的渲染器里
退化成**豆腐块**(本机截图对比可见)。试过给图标指定 Apple Color Emoji、
全局字体栈加 !important、乃至 font-variant-emoji —— 分别无效或更糟
(最后一招把 ASCII 数字也拆成了 emoji 呈现)。而「哪套字体栈一定带 emoji」
跨平台并不成立,与其赌字形,不如不用。
emoji 图标 → 路由表 meta.icon 字段整体移除,侧栏改纯文字
n-empty 空态插画 → 16 处 :show-icon="false"(空态里的插画比图标更「图形」,
分布在 9 个页面,是最容易漏掉的一类)
n-alert 类型图标 → 7 处(naive-ui 的 showIcon 默认 true,不显式关就在)
n-popconfirm 询问图标 → 3 处
夹在文案里的 ⚠ → TerminalView 的「高权限操作,请谨慎」保留纯文字
严重程度不靠图标也能读:n-alert 仍由 type 的底色/边框区分。
顺带清掉随包的模板残留资产:
public/vite.svg、public/tauri.svg —— public/ 下的文件会被整体复制进产物,
此前等于把 Vite / Tauri 的 logo 一并发出去了(且 index.html 的 favicon 正指向它);
src/assets/vue.svg —— 无人引用;
index.html 的 <title> 还是模板默认的「Tauri + Vue + Typescript App」。
原生窗口标题取自 tauri.conf.json("nodeagent 控制台"),故此前不影响观感,
但与 dev 态浏览器标签页不一致,一并改齐。
「不属界面图标、刻意未动」的三类写明在 CHANGELOG 里:
托盘图标(菜单栏没图标就点不到,关窗后应用无法寻回,属功能必需)、
被控端 CLI 自身打印的文本标记(⚠️/ℹ️/✅,是程序输出内容,非界面元素)、
控件的结构性部件(下拉箭头、折叠箭头、输入框清除钮、表格排序箭头)。
验证:vue-tsc 0 错 | 契约守卫 6/6 | 单测 280/280 | 前端产物内已无任何 svg 与 logo 字样 |
macOS .app 重打通过(92.21 MiB,前端在无 public/ 下亦正常构建)。
|
2026-10-11 13:50:49 +08:00 |
|
jkooe
|
2528f6b709
|
docs: 归档「控制台去命令行化」—— CHANGELOG / console-plan / README
CHANGELOG:新增 [未发布] 章节,并按「刻意不升版本号」写明理由 ——
本轮新增的全是控制台功能,被控端能力集合未变;而 bundle-agent.mjs 会把根
package.json 的 version 注入 agent.mjs(__AGENT_VERSION__),
一旦为控制台功能升号,被控端就会谎报一个它并未包含的版本。故维持 2.0.2。
console-plan:新增「本机服务托管」章节,含需求逐条落点对照表、
消灭命令行窗口的三条措施、四条实测踩出来的约束、播种防降级的规则、
「不靠单一信号判实例在跑」的交叉判定、以及仍需图形环境人工实点的清单。
README:控制台页面表 7 → 9(补「本机服务」与「服务日志」),
打包命令改为 pnpm --filter nodeagent-console bundle,
体积数字修正 —— 原文写的 .app ≈ 6.8 MiB 是 Node sidecar 方案之前的旧数,
现为 .app 90.8 MiB / .dmg 33.78 MiB,并补「无需预装 Node」「自启与图形能力
要求登录用户会话」两条注意事项。
|
2026-10-11 11:42:27 +08:00 |
|
jkooe
|
ad569d4e6d
|
ci: 控制台资源断言 2→3,并补 @nodeagent/agent 构建前置
控制台随包分发的资源由 2 份增至 3 份(sidecar.mjs / node / agent.mjs):
- rust.yml 的资源项断言同步改为 3,且不再只数个数 ——
改为断言目标名确实包含 sidecar.mjs 与 agent.mjs、以及 node/node.exe 二者任一。
只数个数的话,「三份但错了一份」照样过。
顺序依赖(本地验证发现不了的一类):
prepare:node 现在还要 esbuild 打包被控端,故 @nodeagent/agent 的 dist 成为新的
前置产物。本地验证时 dist 早就在,会把这层依赖完全掩盖 —— 故在 steps 里显式写全
构建步骤,并加注释说明为什么顺序不能调。
rust.yml 的 paths 触发器补 scripts/lib/bundle-agent.mjs 与 apps/agent/**:
改这两处同样会让控制台打包结果变化,不触发就等于零覆盖。
windows-e2e.yml 同步:Windows 分支现在也覆盖 agent.mjs 的准备与解析。
|
2026-10-11 11:42:14 +08:00 |
|
jkooe
|
2eb6a6f8bb
|
feat(console): 本机被控端托管 —— 启停/重启/日志/自更新/托盘/开机自启
目标:双击应用即用,全程不碰命令行。
要求 落点
不再打开命令行窗口 agent.rs 的 CREATE_NO_WINDOW + 自启拉控制台而非裸 node
启动/停止/重启 AgentManager 五态状态机 + 界面按钮 + 托盘菜单
检查更新 check_update / download / apply_update
日志功能 stdout/stderr 双泵 + 环形缓冲 + 实时推送 + 过滤/导出
后台运行 托盘常驻,关窗最小化而非退出
开机自启 macOS LaunchAgent / Windows 计划任务
新增「本机服务」页(/local,默认首页)与「服务日志」页(/logs)。
默认路由由 /nodes 改为 /local:打开应用先看到本机被控端的启停面板,
而不是先要求用户填一个连接地址。
四条实测踩出来的关键约束(均已在代码里注明原因):
1. 退出监控用轮询 try_wait,绝不把 Child 交给 wait()
—— wait() 会取得 Child 所有权,此后 stop() 再也拿不到它去 kill。
2. 日志游标用独立单调 seq,不用位置下标
—— 环形缓冲淘汰旧行后位置下标会漂移,前端增量拉取静默错位(不报错)。
3. start() / restart() 的接收者必须是 self: &Arc<Self>
—— 监控任务要持自持引用。
4. macOS gui/<uid> 的 uid 运行时 id -u 现取,不硬编码 501。
播种(seeding)防降级:只有「随包版本 > 已安装版本」才覆盖数据目录里的脚本,
且版本比较按数值逐段(2.0.10 > 2.0.9)。若写成「同版本才跳过」,
用户自更新过被控端后下次开控制台会被静默降回旧版。
「实例是否在跑」不靠单一信号:管理器状态与 TCP 端口探测交叉判定,
以区分「本控制台托管的实例」与「手工安装的非托管实例」并给出不同处置。
消灭黑窗有三条措施,第 2 条最易漏:Rust 的 std::process::Command 默认不设
CREATE_NO_WINDOW,于是「界面上点一下启动」= 屏幕上闪一个控制台窗口。
控制台现在随包分发被控端脚本(资源 2 份 → 3 份:sidecar.mjs / node / agent.mjs),
用户装完控制台本机被控端即已就绪。
新增 tests/unit/console-commands.test.mjs(6 用例)钉住三类「不报错的失效」:
invoke ⇄ generate_handler! 双向、emit ⇄ listen 双向(这类失效会让界面永不刷新且零报错)、
bundle 资源名 ⇄ Rust resolve_* 字面量、sidecar 方法名、路由表 ⇄ 实际 view 文件。
已注入故障实测会红(改命令名与事件名 → 当场 2 条失败),非空转守卫。
验证:cargo check 0 错 0 警 / cargo test --lib 10·10 通过(含真起 node 子进程)/
clippy -D warnings 通过 / vue-tsc 通过 / 单测 280·280(274 → 280)/
verify-bundle 真 spawn 打包内 node 跑通打包内 sidecar。
⚠️ 托盘、关窗最小化、开机自启、界面点按钮仍需图形环境人工实点。
|
2026-10-11 11:41:56 +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
|
9ae1326be2
|
docs(readme): 版本基线更新到 v2.0.2
v2.0.0(10-10)那条基线已过期。现基线:v2.0.2(2026-10-11,资产修复 ——
v2.0.1 的 arm64 包内实为 x64 node),51 项能力 · 274 单测(本地实跑全绿)。
|
2026-10-11 10:45:48 +08:00 |
|
jkooe
|
1ab9ed22c7
|
docs(vm): 图形能力实测归档 + §9 复验配方
- §7 图形能力行:⏳ 待登录态 → ✅ **登录态下全通**(真实桌面截图 1800×1010 / window.list /
screen / audit verify),并注明「会话 0 的失败是身份问题,非产品缺陷」
- 修正重复的 §7 编号(→ §8),复位一处错位的表格行
- §6 补三条排查项:x64 Node 的 Prism 现象、`screen.capture` 的会话身份、
**微软账户不支持传统 autologon**(MSA 凭据走 CloudAP,`LogonUser` 恒返回 1326)
- 新增 §9「图形能力复验配方」:本地账户 autologon → 计划任务 Interactive 启动 →
三件核对 → 证书指纹变更的处置 → 验证完的安全清理
|
2026-10-11 10:32:48 +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
|
f85acad5db
|
chore(release): v2.0.2
v2.0.2
|
2026-10-11 10:18:28 +08:00 |
|
jkooe
|
f558e3e7df
|
chore(release): 交付物架构断言 + CHANGELOG 归置 v2.0.2
已公开发布的 nodeagent-win-arm64.zip 内装的其实是 x64 的 node.exe
(两包内 node.exe 字节完全相同,PE Machine 均为 0x8664),该包在 ARM Windows 上
能启动、能 listen,却 TLS 握手不完成 —— 被控端实际不可用,而 macOS 侧 100% 复现不出。
- 新增 scripts/lib/zip-pe-arch.mjs:纯内置 zlib 手解 zip、读 PE 头判架构。
不依赖 unzip / Expand-Archive(Windows runner 上没有 unzip)。
含 CLI 自检入口,已注入故障实测会红(arm64 包去要 x64 期望 → 退出码 1)。
- release.mjs:生成资产前调用之,不符即中止发版 —— 拦在 tag/push 之前,
是「交付物本身」的断言,与 pack.mjs 的「流程内部」断言分两层。
- windows-e2e.yml:把原先内联的 PowerShell 校验实现改为复用同一模块,
消除两套实现各自漂移的可能。
- CHANGELOG:[未发布] 归入 v2.0.2。
|
2026-10-11 10:17:30 +08:00 |
|
jkooe
|
87b74fd015
|
docs(vm): B5 按实测改写 —— arm64 包原生可用 / win-x64 包 TLS 握手不通
原 B5 的假设是「x64 包在 ARM 上也许能跑」,本轮实测证伪:能启动、能 listen,
但 TLS 握手不完成(E_TIMEOUT),被控端实际不可用。补对照数据与复验步骤;
同步更新检测表、前置准备(③④ 改用 arm64 包)、结论标注规则与新增 §7 实测记录。
|
2026-10-11 09:48:10 +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
|
e816c1fd39
|
fix(ci): Windows 上补 sidecar:build —— 新加的步骤漏了前置产物
本轮新增的「控制台 Node 运行时(Windows 分支)与自举验证」一步在真实 runner 上失败:
✗ 未找到 D:\a\nodeagent\nodeagent\apps\console\sidecar\dist\sidecar.mjs
根因:prepare-console.mjs 的前置检查要求 sidecar 已构建,而该步被排在了构建 sidecar 之前
(rust.yml 里恰好已有「构建 sidecar」在前,所以那边没暴露)。
教训(本地跑过 ≠ CI 跑得过,又一例):本地验证时 sidecar/dist 早已存在,
把「步骤之间的产物依赖」这一层完全掩盖了。**新增 CI 步骤时,应逐条核对
「该步依赖的产物由哪一步生成、顺序是否确实在其后」,而不是在已有产物的本地环境里跑一遍就认为通过。**
|
2026-10-11 09:00:09 +08:00 |
|
jkooe
|
a6be917637
|
docs: 记录控制台 Node 运行时分发与体积口径调整
- console-plan.md:
· §8.2 打包体积指标口径由「Mac .app < 30MB」改为「**分发件** Mac .dmg < 40MB」
—— 原数字是 Tauri + Rust client 时代定的(当时应用内不含 JS 运行时);
改 sidecar 方案后应用必须自带 Node 运行时,.app 因此到 90.8MB,但用户下载的 dmg 仅 33.8MB。
已明确标注「口径调整,待确认」。附本机 Electron 运行时本体 207MB 的实测对照。
· 新增「Node 运行时分发」节:做法、三处实测约束(编译期契约 / strip 须重签 / 映射须对齐
resolve_node)、验证方式(verify-bundle 真跑产物)、实测体积。
· 「已知待办」由两条收敛为一条(剩 GUI 端到端人工验证,需图形环境)。
- CHANGELOG.md:新增 [未发布] 节,记录运行时分发功能与两处规避(SIGKILL 137、编译期契约)。
|
2026-10-11 08:55:45 +08:00 |
|
jkooe
|
8664ec2926
|
ci: 覆盖 Node 运行时准备与「产物自举」(Linux + Windows 双覆盖)
新增 runtime 准备脚本后,两件事此前零覆盖:脚本本身在 Windows 上的分支、
以及「打包产物能不能真跑」。补上:
- rust.yml:prepare:node 真跑(验证脚本可用 + 产物自检)→ 断言注入片段声明两份资源
→ `verify-bundle --runtime` 真 spawn 运行时跑 sidecar 走一次 JSON-RPC。
paths 触发加上 scripts/prepare-console.mjs。
注:Linux 上不走 strip 分支(macOS 专属),故「strip 必须 adhoc 重签」由 macOS 本机实测覆盖。
- windows-e2e.yml:新增一步跑 prepare:node + verify-bundle --runtime —— 该脚本在 Windows
上走的是「复制 node.exe、无 strip」的另一条分支,此前从未被真实 runner 验证过。
|
2026-10-11 08:55:38 +08:00 |
|
jkooe
|
22bc732334
|
feat(console): 控制台随包分发 Node 运行时(目标机器无需预装 Node)
控制台的 sidecar 是 .mjs,此前只能在装了 Node 的机器上跑(resolve_node 会回退系统 node)。
新增 scripts/prepare-console.mjs 把 Node 运行时准备为 Tauri resource,随包分发给最终用户。
- prepare-console.mjs:复制本机 node → apps/console/src-tauri/runtime/,macOS 上 strip -x
(108MB→86MB)**并 adhoc 重签**,再生成注入片段 tauri.bundle.gen.json;带幂等(同源跳过)
与**产物自检**(真跑一次 node,失败即删除产物并报错,避免坏产物被静默复用)。
- apps/console/scripts/verify-bundle.mjs:验证「打包产物能否自举」——真 spawn 打包内的 node
去跑打包内的 sidecar 并走一次真实 JSON-RPC 握手。
- package.json:新增 prepare:node / verify:bundle,bundle = sidecar:build → prepare:node →
tauri build --config → verify:bundle(一条命令产出可分发件并自证可用)。
- src-tauri/.gitignore:忽略 /runtime/(~86MB)与生成的注入片段(生成物不入库)。
三处实测踩出来的约束(均已写进脚本注释与 console-plan.md):
1. **macOS 上 strip 会使 Mach-O 代码签名失效 → 内核直接 SIGKILL(exit 137)**,且无任何有用报错。
实测对照:原样复制 ✓ / strip 不重签 ✗(137) / strip + `codesign --force --sign -` ✓。
故 strip 之后的 adhoc 重签是**必要配套**,不是可选优化。
2. **Node 运行时不得声明进默认 tauri.conf.json** —— bundle.resources 是编译期契约
(tauri-build 校验声明的文件必须存在),声明「运行期才就绪」的资源会让 cargo check/test
在 build.rs 阶段直接失败(2026-10-10 已踩)。故一律走 `tauri build --config` 注入。
3. **resource 映射必须与 resolve_node 对齐** —— `runtime/node → node` 使产物落在
Contents/Resources/node,正是 src-tauri/src/sidecar.rs 里 resolve_node 查找的路径。
实测(macOS arm64):.app **90.8 MiB**(原 6.75 MiB,增量即运行时 86MB)/
.dmg **33.78 MiB**(分发件,仍低于 40MB 目标);应用进程实测可启动且无崩溃输出。
对照:本机实测 Electron 运行时本体(Electron Framework.framework)207MB —— 不到一半。
|
2026-10-11 08:55:31 +08:00 |
|
jkooe
|
4f4d8913b5
|
chore(release): v2.0.1
v2.0.1
|
2026-10-11 08:02:19 +08:00 |
|
jkooe
|
f4ae20969a
|
chore(release): release.mjs 支持双架构(同时出 x64 + arm64 包)+ CHANGELOG v2.0.1
## release.mjs
- 打包阶段改为循环 `pack.mjs --arch x64|arm64`(原先只出 x64)
- 资产新增 `nodeagent-win-arm64.zip`;`latest.json` 新增 `zip_arm64` 字段
(**只增不减**,符合 VERSIONING 的兼容铁律;旧控制端忽略即可)
- RELEASE_NOTES 提示按架构选包(x64 包在 ARM Windows 上 TLS 握手不通)
- 顺手修一处改名遗漏:`zipPath` → `zipX64` 后,产物存在性检查仍在用旧名(会 ReferenceError)
## CHANGELOG
补 v2.0.1 段:3 个修复(control.ps1 解析期错误 / 连接保活缺 pong / v26 清理阈值过粗)
+ 2 项新增(`pack --arch`、`verify-b2.mjs`)+ 新增文档 `VM-VERIFICATION.md` + 真机验证状态表
|
2026-10-11 08:00:52 +08:00 |
|
jkooe
|
7d13a48caa
|
docs(changelog): v2.0.0 的「已知限制」更新为「验证状态已补齐」(2026-10-11 VM 真机验证)
|
2026-10-11 07:53:42 +08:00 |
|
jkooe
|
40b7db16c1
|
docs(roadmap): §五/§九 的 7 处历史「未做」标注实际状态(避免误导为待办)
这些行是当时的计划快照,内容其实都已实现(screen.find 属性化、条件组合、方向二/三、ed25519 默认化
以及 v1.5 真机验证)。不改写历史结论,只更新事实以免读者误判为未完成。
|
2026-10-11 07:53:10 +08:00 |
|
jkooe
|
765ad08ee6
|
docs(roadmap): §九 台账 —— v1.5/v26 真机验证标记完成(VM Win11 ARM)
|
2026-10-11 01:13:36 +08:00 |
|
jkooe
|
fbcb8f8408
|
fix(v23): 连接保活补 pong 检测(治僵尸连接占名额)+ B2 三防护真机验证通过
## 真机发现(Parallels Win11 ARM,B2 验证过程中撞到)
心跳只发 `ping()` 而**不检查 pong**,且 ws 库默认不会因「ping 无 pong」断开 ——
客户端进程已死(TCP 未发 FIN)时,连接会一直挂在 `states` 里占 `max_connections` 名额。
叠加「有任务/订阅时清扫保守跳过」的既有策略 → `states.size` 虚高 → **把正常控制端挡在外面**
(本次实测就把自己锁在外面了)。
## 改法
- `attachConnection` 里加 `ws.on('pong')`:**客户端回应也算活跃**(更新 lastActiveAt)
- 心跳里:若某连接静默超过 `idle_timeout_ms * 2` → `ws.terminate()`(标准 ws 保活)
→ 僵尸连接不会再永久占位
## B2 三场景真机验证(全部通过)
- ① **握手封禁**:配 `max_attempts=3` → 3 次错误密钥被拒后,**正确密钥也被拒** ✓
- ② **连接上限**:配 `max_connections=6` → 第 1~6 个成功、**第 7 个被拒** ✓
- ③ **空闲断开**:配 `idle_timeout_ms=20000` → 静默 **40 秒**被断开 ✓
(符合 20s 阈值 + 30s 清扫周期)
- 新增 `scripts/verify-b2.mjs`(三模式:idle / maxconn / ban),全部走**真实连接**
## 验证过程中另修的两个自测问题(都记进文档)
1. 脚本默认 URL 写成了 `ws://` —— 被控端 `tls:true`,**必须 wss**(否则 socket hang up)
2. 测 `max_connections` 时值太小会**把自己锁在外面**;且被拒连接会被计入握手失败形成恶性循环
→ 结论:测试要留余量,并给自己配 `auth_ban.whitelist`
## 收尾
测试用的 `security` 段已用**备份恢复**(`agent.json.before-b2`);pong 修复已 `deploy` 到 VM
(指纹 abca2b8168f0)。
|
2026-10-11 01:13:12 +08:00 |
|
jkooe
|
ab7a2b147b
|
fix(v26): 自重启任务清理阈值改为「本 agent 启动时刻」(真机实测 1 小时粗阈值仍会累积)
## 真机发现(Parallels Win11 ARM)
restart 两次后仍有 **2 个** selfrestart 任务(旧的 Ready 未被清)。诊断确认:
- 清理**条件**能选中它 ✓(非匹配问题)
- **手工删除**成功 ✓(非权限问题)
→ 算时间戳才明白:那是「1 小时」粗阈值的**必然结果** —— 旧任务创建于 00:44,
第二次 restart 在 00:56(仅隔 12 分钟),清理那一刻它**仍是 Running**(cmd 还持有上一次的 agent),
不满足「非 Running 或早于 1 小时」→ 保留。
**但规则本身是缺陷**:频繁 restart(间隔 < 1h)时旧任务照样累积 ——
v26 想治的僵尸只是从「永不清理」变成「1 小时后才清」。
## 改法(语义更精确)
```powershell
# 旧:$cut = (Get-Date).AddHours(-1) ← 粗略
# 新:$cut = (Get-Date).AddMilliseconds(-<agent uptime>) ← 「本 agent 的启动时刻」
```
凡在**我启动之前**就运行的任务,必然不是我的宿主(我的宿主刚刚才创建)→ 可安全删除。
## 验证(真机闭环)
1. `nodeagent deploy release/agent.mjs` → 指纹校验 + 备份 + 上传 + 重启 + 校验 ✓
(顺带把 deploy 机制也验了)
2. `restart` → **任务只剩 1 个**(Running,当前宿主)→ **旧任务被精确清理** ✓✓
3. 单测:断言必须是「agent 启动时刻」阈值,且**禁止回退到 AddHours(-1)**;
守卫 9/9 + 全量 274 绿;7 包构建通过
|
2026-10-11 00:50:51 +08:00 |
|
jkooe
|
047cc9efd2
|
docs(vm): 补 ARM64 包的解决方案(pack.mjs --arch arm64)
|
2026-10-11 00:32:54 +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
|
54f7f4c7ab
|
test(vm): Parallels Win11 ARM 真机验证 —— A 组 15/0 + v1.5 四项全过 + 锚点端到端通过
## 环境
Parallels 27 + Win11 ARM,VM 10.211.55.9(共享网络);被控端 `-NodeId win-arm -AllowInput`。
## ⭐ B5 结论(与预期不同,但更有价值)
| 组合 | 结果 |
|---|---|
| `win-x64` 包(内含 x64 Node) | ⚠️ **能启动、能 listen 8765,但 TLS 握手 60s 不完成** → 协议层不可用(Prism 下) |
| **换 ARM64 Node(同版本 v22.20.0)** | ✅ **完全正常**:握手成功、连接、51 项能力 |
× 结论:`nodeagent-win-x64.zip` 不能在 ARM Windows 上实用;ARM 上需 ARM64 Node。
## 验证结果
- **A 组(verify.mjs 17 项)**:✅ 15 通过 / 0 失败 / 1 跳过(29.5s)
- **B1 v1.5 四项**:✅ 全过 —— 字符串热键 / 预设别名(`copy→ctrl+c` 映射正确)/ 长按 /
后台定向投递(**explorer 命中 138 窗口**、WindowsTerminal 10 窗口)
→ **v1.5.0「未经真机就发版」的债,至此还清**
- **B4 锚点端到端**:✅ collect(62 条) → 上传云盘 → verify 判定「链已增长」+ 精确明细
- B2/B3:⏸ 阻塞(需 restart,而计划任务启动失败 —— 手工跑却完全正常)
## 本轮真机抓到的 2 个真 bug(都已修)
1. `control.ps1:100` 的 `$scheme:` 被 PowerShell 当 drive 引用 → **解析期错误 → 整个脚本不可用**
(stop/start/status 全废);已修 + 加守卫(扫全部 .ps1 的 `$ident:`)
2. `anchor-audit.mjs` 调用**不存在的** `audit head` 子命令 → 改为 `invoke system.audit.head`
(这种「能力存在但 CLI 无子命令」的错配,不真跑一次发现不了)
## 文档
`docs/VM-VERIFICATION.md` 补「实际验证结果」节 + 5 条新增排查项(安装/密钥/CLI 语法/计划任务)
|
2026-10-11 00:18:43 +08:00 |
|
jkooe
|
c8aba83589
|
fix(control): control.ps1 的 $scheme: 被当 drive 引用 → 整个脚本不可用(真机踩到)
## 现象(Parallels Win11 ARM 真机)
`control.ps1 -Action stop/start/status` 一律报:
所在位置 control.ps1:100 字符: 36
+ Write-Host " 协议 : $scheme://0.0.0.0:$Port"
变量引用无效。':' 后面的变量名称字符无效。
## 根因
PowerShell 把 `$scheme:` 解析为 **drive 引用**(如 `env:`),而 `scheme` 不是 drive → 语法错误。
且这是**解析期**错误 → **整个脚本无法运行**(不是只有那一行),stop/start/status 三个子命令全废。
修法:写成 `${scheme}://`(用花括号界定变量名)。
## 改动
- `scripts/control.ps1:100`:`$scheme://` → `${scheme}://`,并加注释说明为什么必须用花括号
(注释里刻意**不写**危险字面量 —— 否则会被新守卫命中,这点在验证时真踩到了)
- 新增守卫(`tests/unit/security-hardening.test.mjs`):扫全部 `.ps1/.psm1`,
禁止 `$ident:` 形式(白名单 `env`/`nuget`/`psitem`/`PSScriptRoot`)
## 验证
- 守卫 **9/9 绿**;全量单测 **274 绿**;ps1-encoding 保持通过(BOM 未被破坏)
- **注入故障实测**:把 `${scheme}` 回退成 `$scheme:` → 守卫**立即红**(失败数 1)→ 恢复后绿
(按项目纪律:没验证过会红的守卫等于没有)
- 全仓重扫同类模式:**仅此 1 处**
|
2026-10-11 00:05:42 +08:00 |
|
jkooe
|
3b05f5b373
|
fix(docs): VM 标注插错位置 —— 移到 §九 台账的正确条目之后
上一提交的锚点匹配过宽(匹配到版本台账表里的「ci: 补全 Windows 真机验证」行),
导致标注插在了错误位置。现改为精确锚点「清理策略**真机验证**」,并复核只出现一次。
|
2026-10-10 23:39:48 +08:00 |
|
jkooe
|
7eb773099f
|
docs(roadmap): 补上「VM 可补验证」标注(上一提交因文本未匹配而未生效)
|
2026-10-10 23:38:17 +08:00 |
|
jkooe
|
ba57d99806
|
docs: 新增 VM 验证清单(Parallels 27 + Win11 ARM)—— 真机不可用时的替代路径
## 背景
Windows 被控端离线导致 4 项验证卡住(v1.5 四项 / v23 三防护 / v26 清理 / 锚点端到端)。
Parallels Desktop 27 可跑 **Windows 11 ARM** 虚拟机(Apple silicon 只支持 ARM 版 Windows,
x86_64 仅 Pro/Business 的早期预览),可提前跑掉**架构无关**的验证项。
## 清单内容(docs/VM-VERIFICATION.md)
- **能补 / 不能补的矩阵**:协议层与输入层可验;**杀软交互、显示/DPI、性能指标不可替代**
- 前置准备:桥接网络(否则 UDP 广播发现测不了)、优先 ARM64 Node、run-portable/install 二选一
- **逐条命令**(均已对着源码核实参数):
- A 基础验收:直接复用既有 `scripts/verify.mjs`(17 项 + Markdown 报告)
- B1 v1.5 四项:`key press "ctrl+shift+esc"` / `copy` / `--hold 800` / `--route post --pid N`
(对应 args 的 hotkey / preset / hold_ms / route+target_pid 四个分支)
- B2 v23 三防护:`agent.json` 的 `security.auth_ban` 与 `idle_timeout_ms` + 三场景判定
- B3 v26 清理:`restart` 后 `schtasks /query`,**重点看"当前宿主任务必须仍在"**(否则即误杀)
- B4 锚点端到端:`anchor-audit.mjs collect` → 云盘 → `verify`
- B5 额外结论:**win-x64 包在 ARM Windows 上能否跑**(走 Prism;跑通则写进 README)
- **结论标注规则(强制)**:VM 验过的写「VM 已验」;杀软/显示/性能**不得**因 VM 通过就标已验;
并注明「VM 与真机架构不同(真机 x86_64),VM 通过不自动推出真机通过」
- **已知 VM 环境差异表**(排查先看):广播发现需桥接、坐标为逻辑验证、性能数字不可采信等
README 文档区与 ROADMAP §九 均已链入。
|
2026-10-10 23:38:02 +08:00 |
|
jkooe
|
28ce95f045
|
feat(audit): 审计锚点外部化落地 —— 判定逻辑收敛为 protocol 纯函数 + 控制端工具 + 云盘链路
## 为什么(一句话)
锚点的安全性完全来自「它在攻击者碰不到的地方」。与被审计日志同机的锚点等于没锚:
被控端一旦被 root,重写日志后顺手改掉锚点即可。
## 本次改动
### ① 判定逻辑收敛(消除「两份真相」)
把 `compareWithAnchors` 的三条判定抽到 **`packages/protocol/src/audit-anchor.ts`** 的
**纯函数 `judgeAnchors(head, anchors)`**(无 IO,不知路径):
- agent 端 `compareWithAnchors` 改为「读文件 + 调 judgeAnchors」
- 控制端脚本同样调用它
→ 同一份日志不可能出现「被控端说一致、控制端说被改」的分歧
- 附带 `parseAnchorsFromJsonl`(坏行跳过,一行坏数据不毁整份锚点)
- agent 侧类型改为 re-export,既有导入路径不变(**9 个原有锚点单测全绿,行为零变化**)
### ② 控制端工具 `scripts/anchor-audit.mjs`
- `collect`:拉链头 → 生成一行锚点 JSONL(并打印 `ANCHOR_JSON=` 便于 automation 取用)
- `verify`:读锚点文件(可从云盘下载)+ 拉链头 → 判定,`ok:false` 时**退出码 2** + 醒目告警
- 走 CLI 而非自己实现连接(复用其多设备配置/证书钉住/daemon,避免又一份实现)
- 支持 `--head-file`:**离线也能验证判定链路**(本轮即用它做了 fixture 验证)
### ③ 云盘链路
- 建 `…/Project/NodeAgent/anchors/`(与全量备份分开目录)
- 每日 automation 更新为「拉链头 → 上传云盘(一天一个带日期的文件)→ 简报」,
并规定每月首日额外做一次「下载历史锚点 → 链外比对」
## 验证
- **纯函数重构零行为变化**:原有 audit-anchor 单测 9/9 全绿;全仓 273 单测全绿;7 包构建
- **工具离线验证**(fixture):一致 → 「与锚点完全一致」退出码 0;
篡改 → 「链头哈希与锚点不一致」退出码 2;三个错误分支均给出明确提示
- 云盘:`anchors/` 目录创建成功(1/1 步),目录内容已复核
- ⚠️ 端到端(真拉链头 + 真上传)**待被控端上线**;脚本的 CLI 分支已做错误处理验证
|
2026-10-10 19:14:09 +08:00 |
|
jkooe
|
2fe717cb62
|
fix(ci): 修 clippy 的两个错误(含一个真实并发问题)+ 本地补齐 lint 验证
## CI 失败的真实原因(上一轮我误判为 resources,实为 clippy)
`-D warnings` 下两个错误:
1. **`clippy::await_holding_lock`(sidecar.rs,真问题)**:
`if let Some(child) = self.child.lock().unwrap().take() { child.kill().await }`
—— MutexGuard 存活到 if 块结束,**跨 await 持有 std Mutex**:不仅触发 lint,
真会阻塞 executor。已改为「先取出 child(锁随即释放),再 await kill」。
2. `clippy::doc_lazy_continuation`(lib.rs 文档注释):列表与后续段落之间缺空行。
## 我的验证方式错在哪(已修正)
上一轮我本地只跑 `cargo test`,**没跑 clippy** —— 而 CI 是 `clippy -D warnings` + `test`
两道闸。本地与 CI **标准不一致**,于是"本地绿、CI 红"。
本轮起:**本地按 CI 的完整命令集验证**(clippy --all-targets -- -D warnings → cargo test)。
## 验证
- `cargo clippy --all-targets -- -D warnings` → **零警告**
- `cargo test` → **3/3 通过**
|
2026-10-10 15:33:11 +08:00 |
|
jkooe
|
9bba53d5c0
|
fix(ci): 修 Rust Shell 工作流 —— resources 只声明构建期必产出的文件
CI 失败根因(本地已复现确认):`tauri-build`(build.rs) 会在**编译期校验**
`tauri.conf.json` 的 `bundle.resources` 声明文件是否存在。
我之前声明了两项:`sidecar.mjs`(CI 未生成)+ `resources/node`(Node 运行时未下载)
→ build.rs 直接失败("failed to run custom build command")。
修复:
- `tauri.conf.json` 的 resources **只留 sidecar.mjs**,并写明为何不能声明未就绪的文件
- `rust.yml` 在 clippy/test **之前**加「构建 sidecar」步骤(`pnpm --filter nodeagent-console sidecar:build`)
- console-plan.md:⑤ 不再把 node 记为已完成(未验证的不写成做完),
补「已知待办」(Node 运行时随包分发 + GUI 端到端人工验证)与「构建约束」说明
本地复现验证:删掉 `sidecar/dist/` → `cargo check` 复现同一错误;恢复 → 通过 ✓
|
2026-10-10 15:25:12 +08:00 |
|
jkooe
|
53bf5a0634
|
feat(console)!: Rust 壳改用 Node sidecar(选项 D 完成)—— 退役 crates/nodeagent-client
## 动机(消除「两套协议实现」)
原设计让 Rust 壳自己实现 client,与 TS 版(@nodeagent/client)并存 → 改一处漏一处时
构建过、单测过,只在真机暴露。现 Rust 壳只做**进程管理 + 消息转发**,协议实现只剩 TS 一份。
## ④ Rust 壳改造
- 新增 `src-tauri/src/sidecar.rs`(~290 行):Sidecar 管理器 —— spawn node、
行分隔 JSON-RPC 的请求/响应匹配(oneshot + 超时兜底)、通知回调(state/log/agent_event)、
`resolve_node`/`resolve_sidecar` 路径解析(env → Tauri resource → 开发期系统 node)
- 重写 `lib.rs` 为薄转发层:`ensure_sidecar` 懒启动;**5 个 command 签名与 3 个 event 名
(na-state/na-log/na-event)一律不变 → 前端零改动**;`get_capabilities`/`get_state`
读本地缓存以**保持同步签名**;`RunEvent::Exit` 时优雅 shutdown(kill_on_drop 兜底不留孤儿)
- `Cargo.toml`:移除 nodeagent-client 依赖、加 tokio;**版本 0.1.0 → 2.0.0**
(原先与 tauri.conf.json 的 2.0.0 不一致,属版本号统一范围)
## ⑤ 打包配置
- `tauri.conf.json`:`bundle.resources` 声明 sidecar.mjs 与 node;
`beforeBuildCommand` 串上 `sidecar:build`(否则 resource 缺失)
## ⑥ 退役
- 删除整个 `crates/`(12 文件,含 nodeagent-client 与其 probe 示例)
- `rust.yml` 由「Rust client 单测」改为「**Rust 壳单测(真跑 sidecar)**」:
装 Tauri 系统依赖 + Node + pnpm,跑 clippy `-D warnings` 与 `cargo test`
- `package.json`:`test:rust` 改指 console 壳;移除 `probe`
(其「对真机逐项核对字段」的用途已由 CLI `nodeagent invoke` 覆盖)
- 引用清理:README(数字与协议客户端说明)、console 前端两处注释、
console-plan(开头加「原方案已被取代」提示 + 补改造详情)、ROADMAP 历史描述加退役批注
## 验证
- **`cargo test` 3/3 通过**(真跑 Node sidecar 子进程:spawn/请求-响应/错误不崩)——
这是关键:sidecar 管理器**不依赖 Tauri GUI**,故核心逻辑可在 CI 真实验证,不必等人工开窗口
- `cargo check` 零警告;clippy 标准与 CI 一致
- 273 单测 / 7 包构建 全绿
- ⚠️ **仍需人工验证**:Tauri GUI 端到端(开窗口 → 连被控端 → 点界面),沙箱跑不了图形应用
|
2026-10-10 15:16:24 +08:00 |
|
jkooe
|
096193a8ec
|
feat(console): 采纳 sidecar 方案(选项 D 第一步)—— Node sidecar 实现 + 8/8 真机 e2e
## 为什么
原设计让 Rust 壳自己实现一份 client(crates/nodeagent-client),代价是**两套协议实现静默漂移**
(改一处漏一处时构建/单测都不报错,只在真机暴露)。sidecar 让 Rust 壳退化为
「进程管理 + 消息转发」,协议实现只剩 TS 一份,从根上消除漂移。
## 本次交付
1. `apps/console/sidecar/main.mjs`(190 行):把 `@nodeagent/client` 暴露成
**JSON-RPC 2.0 over stdio**(行分隔)。方法 connect/disconnect/invoke/state;
onStateChange/onLog/onEvent 三个回调 → `event` 通知(kind=state/log/agent_event)
2. `apps/console/sidecar/build.mjs`:esbuild **JS API** 打成单文件(避开 pnpm `.bin` 的
ENOENT 坑),并注入 `createRequire` shim —— 否则 `ws` 的动态 require 会报
"Dynamic require of events is not supported"
3. `apps/console/package.json`:加 `@nodeagent/client`/`protocol` 依赖 + sidecar:dev/build 脚本
4. `docs/console-plan.md`:补「实施决策」节(决策理由、协议定义、进度表、打包方向)
## 两条协议纪律(写进注释与文档)
- **stdout 只写协议消息**,日志一律 stderr(混入非 JSON 行会让 Rust 侧解析失败)
- 任何异常都转成响应,**绝不因单个请求崩进程**
## 错误形态归一(实测中修正)
被控端对未知能力/ACL 拒绝/超时是回 JSON-RPC error → `client.invoke()` 抛异常。
若原样上抛前端要处理两种形态,故归一为 `{status:'failed',error:{name,message,data?}}`;
而「未连接/缺参数」仍走协议错误(语义不同)。
## 验证(真实链路,非 mock)
本地 agent → sidecar **8/8 全绿**:connect 回传 51 项能力与 agent_version ✓ ·
invoke system.info ✓ · state 反映已连接 ✓ · 事件通知已推送 ✓ ·
非法能力返回 failed 且不崩 ✓ · disconnect ✓ · **stdout 无日志污染** ✓
打包产物拷到仓库外**独立运行通过**(227KB 单文件自足)。
## 待办(下一批)
Rust 壳改造(command 签名不变 → 前端零改动)· tauri.conf.json 的 resource 配置 +
Node 运行时打包 · 退役 crates/nodeagent-client
|
2026-10-10 15:11:39 +08:00 |
|
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 |
|
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
|
11127b1a89
|
chore(privacy): 杀软实名与网卡名示例泛化(安全报告第十三轮 P2 两项)
## 甲1 杀软实名泛化(6 行)
`主流杀软 / 杀软 / 360` → 「主流杀毒软件 / 杀软」:
- docs/REMOTE-LIMITS.md:标题 + 正文 + 表格两处(保留"会拦什么"的实操信息,去掉具体厂牌)
- scripts/release.mjs:278:Release 正文模板里的信任区提示
- apps/cli/src/cmd/ops.ts:398:部署失败排查提示
## 甲2 网卡名示例泛化(13 行)
「Ethernet」→ `Ethernet`(示例)/泛化为"中文网卡名"(讲原理时):
- docs/REMOTE-LIMITS.md 网络适配器表格、ROADMAP 该项标记为已完成
- scripts/rescue-network.ps1 参数说明、automation.ts 的 interface 描述
- apps/agent/src/capabilities/network.ts 注释:改为"脚本里的非 ASCII 字符(如中文网卡名)"
—— **保留"为什么"的教训,去掉具体实况**
- tests/unit/net-scripts.test.mjs:8 处测试数据与正则断言同步改为 Ethernet
## 验证
工作区残留「主流杀软/杀软/Ethernet」= **0 处**;单测 273 全绿;7 包构建通过。
|
2026-10-10 12:26:41 +08:00 |
|
jkooe
|
d9e2103be7
|
docs(roadmap): 同步 v2.0.0 已发布状态
- 头部基线:v2.0.0(2026-10-10 / a515eb2),单测 273,能力 51,CI 双工作流全绿
- §九 台账补「发版 v2.0.0」行 + 「v1.5 四项与 v26 清理策略真机验证仍待补」的显式标记
|
2026-10-10 12:18:28 +08:00 |
|
jkooe
|
a515eb2e63
|
chore(release): v2.0.0
v2.0.0
|
2026-10-10 12:14:02 +08:00 |
|
jkooe
|
3fcfc7d12d
|
chore: CHANGELOG 定版 v2.0.0 + 忽略 .workbuddy/(本地 agent 数据)
- CHANGELOG:「未发布」→ v2.0.0(2026-10-10),补兼容性声明(完全向后兼容)
与**已知限制**(发布时被控端离线 → v1.5 的四项与 v26 清理策略尚未真机验证)
- .gitignore:忽略 .workbuddy/(memory/automations 属本机状态,不应入库;
此前未被忽略,会干扰 release.mjs 的干净工作区检查)
|
2026-10-10 12:12:47 +08:00 |
|
jkooe
|
865862ee7c
|
refactor: 拆 mcp/index.ts(1139 → 530 行),工具清单独立成 tools/defs.ts
## 拆法
`TOOLS` 数组(610 行,占原文件过半)抽到 `apps/mcp/src/tools/defs.ts` 并 export。
保留在 index.ts 的:
- 连接与调用辅助(ensureClient / call / forwardEventToHost)
- toArgs(入参特化)· TOOL_TO_CAPABILITY · resolveToolCall(分发 switch)
- MCP server 定义与 main
**分工**:defs.ts 只管「暴露什么」,index.ts 管「怎么转发」。
## 零漂移的证据
抽出的最大风险是**顺序漂移或漏搬** —— 两者都不会让构建失败,只会静默改变客户端
看到的工具表。故用「工具名序列 sha256」做证据:
拆分前 41 个工具 / sha=69e8533c42c3…
拆分后 41 个工具 / sha=69e8533c42c3… ✅ 序列与顺序完全一致
## 新增回归守卫 tests/unit/mcp-tools.test.mjs(4 例)
- 工具数 41 且名称唯一
- **顺序未漂移**(序列 sha 比对;有意调整须同步更新基线并说明)
- 每个工具都有 name/description/inputSchema 三件套
- inputSchema 普遍声明 additionalProperties: false
## 验证
单测 269 → 273 全绿;e2e 30 全绿;7 包构建通过。
|
2026-10-10 07:50:13 +08:00 |
|
jkooe
|
690f38207c
|
refactor: 拆 window.ts(1057 → 647 行),PS 预加载段按关注点分离
## 拆法(保守:只换文件组织,运行时逐字节不变)
抽出 5 段到 apps/agent/src/capabilities/win-ps/:
- prelude-win32.ts(30 行)Win32 窗口枚举与矩形读取
- body-list.ts(42 行)窗口枚举 JS/PS 体
- prelude-uia.ts(117 行)UIA 控件树 + Get-UiaProps/Test-UiaWhere/Find-UiaByText
- prelude-ocr.ts(37 行)OCR 引擎预加载
- prelude-image.ts(226 行)图像模板匹配(ZNCC 粗到精)
window.ts 保留:工具函数(runPS/runPSRaw/extractJson/warmup)、
WIN_HELPER_PRELUDE 拼接(顺序不变)、windowList / windowFocus / screenFind。
## 为什么这样拆
1. **不拆常驻助手**:window.list / window.focus / screen.find 共用同一个常驻 PS 进程,
预加载段缺一段就会被 esbuild tree-shake,直到运行时才报"找不到类型"(真机踩过)。
故拼接表达式与四段顺序**原样保留**在 window.ts。
2. **用 sha256 做零行为变化的强证据**:拆分前 dump WIN_HELPER_PRELUDE 的
len=15232 / sha=88059028b342…,拆分后**逐字节一致** ✅
3. 保留原有回归测试 tests/unit/win-prelude.test.mjs(8 例)作为长期守卫。
## 验证
- prelude sha256 与拆分前一致(逐字节)
- win-prelude 8/8、单测 269 全绿、e2e 30 全绿、7 包构建通过
|
2026-10-10 07:47:31 +08:00 |
|
jkooe
|
2e6356c7f4
|
docs: REMOTE-LIMITS 的「观测窗口时点性」缓解措施已落地(monitor/log/audit)
该文原写「可让被控端持续采样并落盘……具备实现基础」—— 现已实现:
- monitor.* 定时采样落盘 + 摘要回看(断几次 / 最长断多久 / 值域)
- log.query 在被控端侧过滤日志(不必整份拉回)
- system.audit.anchor 定期锚定链头(防整链重写)
同时补一句仍未解决的:应用层内部状态(当前在等哪个请求)依旧只能从日志反推 ——
monitor 采的是外部可观测量,读不到进程内部语义。
|
2026-10-10 07:38:46 +08:00 |
|
jkooe
|
01e711be77
|
docs: 新增 CHANGELOG(项目此前没有变更日志)
自 v1.6.0 起累计 33 个未发版提交,按 Keep a Changelog 结构整理:
新增(语义可断言 / 状态可回看 / 安全加固三批)→ 修复(五处只有真机能暴露的严重缺陷 +
其他)→ 安全隐私 → 文档。并补记 v1.6.0 / v1.5.0 历史条目(含该版未经真机验证即发布、
|
2026-10-10 00:40:36 +08:00 |
|
jkooe
|
2cb0ed148e
|
chore(privacy): 源码与测试里的真机常量脱敏(机器名 / 真机内网 IP)
路线图 §七 序 7「源码真机常量注入化」的脱敏部分。
## 改了什么
- apps/agent/src/capabilities/network.ts:注释里的真机机器名 → 泛化为「机器名+纯数字」
(保留"为什么不能拼 USERDOMAIN/USERNAME"这个教训本身,去掉具体机器名)
- scripts/rescue-network.ps1:.EXAMPLE 里的真机内网地址 → 文档示例段 192.168.1.100;
网卡名示例由中文默认名改为 'Ethernet'(既避免像真机、又更通用)
- packages/protocol/src/acl.ts:注释里的真机 Mac 地址 → 192.168.1.100
- tests/unit×4:测试数据里的真机 IP 统一换成 192.168.1.x 示例段
## 过程中的两个教训(都记在代码注释里了)
1. **正则转义形态漏替换**:断言里写的是 `192\.168\.0\.88`(转义点),第一轮只替换了
裸形态 `192.168.0.`,导致脚本值变了、断言没变 → 3 个测试红。
2. **机械替换会破坏「跨网段」这类语义测试**:acl 的 `/24 网段匹配` 用例里有一条
本意是「**不同网段不匹配**」,替换后两侧变成同段 → 断言必红。已改回不同网段
并在注释里标注原因,防止下次再被批量替换破坏。
## 验证
单测 269 全绿(复跑);e2e 30 全绿。
|
2026-10-10 00:39:28 +08:00 |
|
jkooe
|
74d878a1c5
|
docs: DEVELOPMENT 能力表补全至 51 项;RETROSPECTIVE 加「短板已全部解决」批注
DEVELOPMENT.md:能力表此前停在 19 项(v5 时期)—— 补齐 20~51(32 行),
按功能分组并标注引入批次与风险图例;表末声明「权威定义以 manifest 为准,能力数由单测守卫」。
RETROSPECTIVE.md:保留 2026-09-28 的原判断(历史价值),
在末尾追加「短板已全部解决」对照表(自持/语义/状态/安全残余 → 各自解决于哪个版本),
并补一条本轮验证出的经验:真机验证不可替代(8 个 bug 只有真机能暴露)。
|
2026-10-10 00:35:43 +08:00 |
|
jkooe
|
dfd3b2349e
|
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」表,便于后续复盘
|
2026-10-10 00:29:39 +08:00 |
|
jkooe
|
32e0dd7bd5
|
fix(v26): 自重启任务清理加时间余量(治真机累积的 Running 僵尸)
## 问题
重启任务的 cmd 会**一直持有新 agent 进程**,所以任务状态长期是 Running。
原清理逻辑只清「非 Running」的(怕误杀当前宿主)—— 代价是真机累积:
2026-10-09 实测 D:\\Nodeagent 上残留 3 个 nodeagent-selfrestart-* 任务(全 Ready/Running)。
## 修法(保守优先)
清理条件改为「**非 Running** 或 **LastRunTime 早于 1 小时**」:
- 1 小时余量远大于一次重启的耗时(正常 <1 分钟),所以"当前宿主"绝不会被误判 ——
它刚刚才运行过,LastRunTime 就是几分钟前
- 清旧 Running 僵尸是安全的:它们的 cmd/node 早已退出,只剩注册表条目
用 includes 断言锁死这三条(AddHours(-1) / LastRunTime -lt $cut / Get-ScheduledTaskInfo),
并禁止回退到"只清非 Running"的老写法。
## 验证
单测 267 → **269** 全绿(新增 2 例源码断言);e2e 30 全绿;7 包构建通过。
⚠️ **该项在 agent 内,且涉及进程清理 —— 必须真机验证后才算可交付**。
当前被控端整机离线(2026-10-10 00:15 ping 100% 丢包),故标注为待验证,未发版。
|
2026-10-10 00:19:43 +08:00 |
|
jkooe
|
68546e63e8
|
docs+fix(v25): 审计锚定的语义说明(锚定自身也写审计 → 紧随比对应显示「已增长」)+ SECURITY §4.8
真机验证时发现一个语义瑕疵:刚锚定就比对,永远显示链已增长 ——
原因是锚定调用自身也被 server 层记入审计(统一记录所有 invoke)。
功能正确(不误报篡改),但反直觉,故:
- compareWithAnchors 的 verdict 补上解释
- manifest 的 audit.anchor 描述补上说明
- SECURITY.md 新增 §4.8(含两个关键设计:四元组锚点防轮转误判、追加式防刷新抹痕),
§5「还没做的」清单至此**全部完成**
|
2026-10-10 00:12:02 +08:00 |
|