Files
wx-win-agent/control-plane
..
2026-09-12 11:09:42 +08:00

WxAgent Control Plane

Go 1.23 控制面与 React 管理端。节点只主动出站连接,控制面不暴露节点端口。

本地启动

cd control-plane
npm --prefix web ci --no-audit --no-fund
npm --prefix web run build
go run ./cmd/wxagent-control-plane

启动前设置(单节点兼容写法):

export WXAGENT_NODE_ID=local-node
export WXAGENT_NODE_TOKEN='local-node-token'
export WXAGENT_WEB_USER=admin
export WXAGENT_WEB_PASSWORD='change-me'

多节点/多用户使用 JSON map

export WXAGENT_NODE_TOKENS='{"node-a":"token-a","node-b":"token-b"}'
export WXAGENT_WEB_USERS='{"admin":"change-me","auditor":"read-only-password"}'

可选:WXAGENT_CONTROL_PLANE_ADDR(默认 127.0.0.1:8090)、WXAGENT_CONTROL_PLANE_DATA(默认 control-plane-data.json)。凭据可通过环境变量或 *_FILE Secret 文件读取,不写入控制面数据文件和日志。节点的 remoteConfigurationFile 仅用于读取预置配置;Windows Agent 不提供命令行修改配置,启动和本机配置必须通过双击 WxAgent.Tray.exe 后的“服务设置...”窗口完成。

生产控制面可直接启用 TLS 和节点 mTLS:

可选的 AI Provider 使用 OpenAI-compatible chat/completions 接口;密钥只从环境或 Secret 文件读取:

WXAGENT_AI_BASE_URL=https://api.openai.com/v1
WXAGENT_AI_API_KEY_FILE=/run/secrets/ai_api_key
WXAGENT_AI_MODEL=gpt-4o-mini
WXAGENT_AI_TIMEOUT=60s
WXAGENT_AI_SCHEDULER_INTERVAL=5s
WXAGENT_CONTROL_PLANE_TLS_CERT_FILE
WXAGENT_CONTROL_PLANE_TLS_KEY_FILE
WXAGENT_MTLS_CLIENT_CA_FILE
WXAGENT_MTLS_REQUIRE_NODE_CERT=true
WXAGENT_MTLS_REVOKED_CERTS_FILE
WXAGENT_NODE_TOKENS_FILE=/run/secrets/node_tokens
WXAGENT_WEB_USERS_FILE=/run/secrets/web_users

节点证书按证书原始 DER 的 SHA-256 指纹逐行写入撤销文件;文件读取失败时节点认证拒绝。控制面还提供 /readyz,并对数据文件使用单写入者锁、自动轮转备份和任务/事件/审计保留期限。生产示例见 compose.production.yml

浏览器访问 http://127.0.0.1:8090/,登录后管理节点、任务、白名单事件和审计记录。生产部署必须使用 HTTPS、节点 mTLS 和外部 Secret 文件;本地 HTTP 仅用于显式的私有网络/loopback 集成测试。

远程只读查询使用同一持久化任务队列,返回 task_id 后通过 GET /v1/tasks/{task_id} 取结果:

POST /v1/reads/sessions   { node_id, account_id, idempotency_key, limit?, offset? }
POST /v1/reads/contacts   { node_id, account_id, idempotency_key, groups_only, contains?, limit?, offset? }
POST /v1/reads/messages   { node_id, account_id, idempotency_key, chat_id, include_content?, limit?, offset? }

节点只返回本地已启用且 identityVerified 的白名单会话;未授权范围在节点读取前拒绝。chat_id 必须使用会话列表返回的稳定会话标识,消息结果是当前微信 UI 可见历史,不是数据库全量历史。联系人查询中的 chat_id 使用联系人数据库稳定 ID;所有响应仍受任务结果大小和分页上限约束。

AI 消息处理

Web 端的“AI 处理”页面用于创建消息处理流程。每个流程绑定一个或多个 node_id + account_id + chat_id + chat_type 目标,支持:

  • realtime:由节点已授权的实时消息事件触发;
  • interval:由控制面按秒间隔创建 read-messages 只读任务;
  • output_schema:根节点为 object 的 JSON Schema,AI 输出会在控制面再次校验;
  • read_message_context:读取本次有限消息上下文;
  • reply_text:在流程显式授权后,向来源群聊/私聊创建一个受控 send-text 任务。

相关接口:

GET    /v1/ai/tools
GET    /v1/ai/flows
POST   /v1/ai/flows
GET    /v1/ai/flows/{flow_id}
PUT    /v1/ai/flows/{flow_id}
DELETE /v1/ai/flows/{flow_id}
POST   /v1/ai/flows/{flow_id}/run
GET    /v1/ai/runs?flow_id=&limit=

AI 处理仍受节点本地白名单约束。定时读取的消息范围与普通 read-messages 相同,只是当前微信 UI 可见历史,不是数据库全量历史。AI Provider 未配置时流程可以保存,但执行记录会明确失败,不会把消息内容写入普通日志。

验证

cd control-plane
go test ./...
go vet ./...
cd ../control-plane/web
npm run build
cd ../..
./scripts/remote-control-smoke.sh

remote-control-smoke.sh 会启动一次临时控制面,运行 .NET 节点协议客户端,验证注册、心跳、任务租约、幂等、结果回传和白名单拒绝;不会操作真实微信联系人。

控制面并发/读取任务基线(使用已注册的测试节点,不执行写任务):

WXAGENT_SCALE_WEB_PASSWORD='<test-password>' \
  scripts/control-plane-scale-smoke.py --base-url http://127.0.0.1:8090 \
  --node-id node-a --account-id account-a --requests 100 --workers 10

control-plane-data.sh backup/restore 用于停机前后的手工备份和原子恢复;自动备份由 Store 按 WXAGENT_CONTROL_PLANE_BACKUP_INTERVAL(默认 5 分钟)产生。当前 JSON 存储使用 active/passive 单写锁,不支持 active-active 多实例共享写入。

Windows 手工验收

  1. 在已登录且未锁定的微信桌面会话中双击 WxAgent.Tray.exe;首次运行或托盘菜单“服务设置...”打开后,配置本机监听、凭据、验证模式和自动锁屏。
  2. 不使用 WxAgent.Host serve--configremote auth setremote reporting enable/allow 或其他命令行配置。WxAgent.Host doctorinspect-uiremote ... show/probe 仅用于只读诊断。
  3. 远程配置若已由部署系统预置,托盘服务读取 remoteConfigurationFile;当前不通过命令行修改远程地址、Token、节点 ID 或 reporting 白名单。
  4. 仅使用文件传输助手、Hao 豪吉祥三宝消息测试专用群组做发送/接收验证:先确认目标账号、稳定 chat ID 和文本;Web/HTTP/MCP 写操作仍必须通过现有确认门禁。
  5. 在控制面取消任务并验证节点不执行;断开网络后恢复,确认未完成副作用任务显示 ResultUnconfirmed,不会自动重放。

未解锁的 Session 0、昵称、PID、窗口句柄和未确认的运行时身份都应视为验收失败;手工验收不得改写微信数据库或绕过安全机制。

容器镜像

.gitea/workflows/build-web-image.yml 会构建并推送 git.ipao.vip/<owner>/<repo> 镜像:主分支额外更新 latestv* 标签额外更新对应版本标签。请在 Gitea 账号级 Secrets 配置 REGISTRY_TOKEN;登录用户名使用仓库所有者。

生产环境不要提交 compose.production.yml 所引用的 secrets/ 文件。将节点 Token map、Web 用户 map、TLS 私钥、客户端 CA 和撤销指纹列表放入外部 Secret 管理或受 ACL 保护的部署目录,再运行 docker compose -f compose.production.yml up -d。证书泄露时可用 scripts/revoke-client-certificate.sh 更新指纹列表,并重建 Docker Secret 挂载的容器。