{ "module": "dashboard", "version": "2.0", "purpose": "AgentsMeeting 管理门户 — Flask app on :5803。监控所有 Agent 的在线状态(跨平台 SSH ejabberdctl + xmpp_bot HTTP API + 本地进程检测)、提供 Web UI(tabs: Agents/Kanban/Infrastructure/开发原则)、管理 Agent 启停(systemctl on Linux, subprocess on Windows)、EasyTier/RDP 远程控制代理、OpenCode Go 用量监控、Kanban 看板、自修复流水线、git 提交历史等。", "ui_location": "主 Dashboard UI (http://127.0.0.1:5803)", "human_help": { "title": "AgentsMeeting Dashboard — 管理门户", "description": [ "AgentsMeeting Dashboard 是所有 Agent(xxm/mohe/zhiwei/xiaoguo)的统一管理门户,部署在 192.168.1.246:5803,同时运行在 Windows 开发机 127.0.0.1:5803。", "核心功能:Agent 在线状态监控(跨平台 SSH ejabberdctl + xmpp_bot HTTP API)、Agent 一键启停、系统健康仪表板(F 健康)、Kanban 看板、Infrastructure 基础设施控制(EasyTier VPN / RDP 远程桌面 / OpenCode Go 用量监控)。", "状态检测三层:① 权威层 — SSH docker exec ejabberdctl connected_users(查谁连着 XMPP 服务器,跨平台可信)② 桥接层 — xmpp_bot /health API(查本地 bot 连接状态)③ 本地层 — 进程扫描(查 Windows 上 python 进程是否存在)。", "自动恢复:连续 3 次检测到离线后,自动重启本地 Agent 服务(Windows: subprocess.Popen; Linux: systemctl start)。", "双轨文档体系:每个功能模块在 specs/{module}.json 维护 human_help(? 按钮—人类看)与 ai_spec(§ 按钮—AI 看),一源双出。", "页面刷新:Dashboard 每 10 秒自动拉取 Agent 状态 + Infrastructure 数据;Kanban 每 15 秒独立刷新。" ], "participants": [ {"name": "莫荷", "device": "Linux 192.168.1.246", "role": "生产环境 dashboard 部署机(systemd 管理) + 公网入口(ags.yoin.fun → nginx → :5803)", "note": "AGENTSMEETING_ROOT 环境变量覆盖项目路径(/home/hmo/projects/AgentsMeeting)"}, {"name": "小小莫", "device": "Windows 192.168.1.16", "role": "开发环境 dashboard + xmpp_bot HTTP bridge (:5802) + EasyTier/RDP 控制代理", "note": "dashboard 本地运行用于开发和测试"}, {"name": "ejabberd", "device": "246 Docker", "role": "XMPP 服务器 — 权威在线状态源", "note": "通过 SSH docker exec ejabberdctl connected_users 查询(Windows)或直接 docker exec(Linux)"}, {"name": "老莫", "device": "任意浏览器", "role": "Dashboard 使用者", "note": "访问 http://192.168.1.246:5803 或 公网 https://ags.yoin.fun"} ], "usage": [ "1. 浏览器打开 http://192.168.1.246:5803(内网)或 https://ags.yoin.fun(公网)", "2. Agents tab:查看所有 Agent 在线状态 + 启停 + 日志。JD 显示在 XMPP 连接 → 绿点在线;显示'离线'→ 点 Restart 尝试恢复。", "3. Kanban tab:查看团队任务看板(数据源 ~/.hermes/kanban.db),支持按状态/负责人筛选", "4. Infrastructure tab:Platform 服务状态 + EasyTier VPN 开关 + RDP 远程桌面开关 + OpenCode Go 用量卡片", "5. F 健康 tab:系统健康仪表板 — 服务概览 / 异常告警 / 全部服务分层展示(通信层/AI网关/辅助服务/定时任务)", "6. G 规范 tab:开发规范文档(dev-spec.md)+ git 历史", "7. H 需求 tab:产品需求文档(PRD.md)+ git 历史", "8. K 测试 tab:自动测试报告(通过 tests_api.py 运行)", "9. 点击模块旁边的 ? 按钮看人类使用说明,§ 按钮看 AI spec" ], "troubleshooting": [ "如果 Dashboard 打不开:检查 5803 端口是否监听 — netstat -ano | findstr 5803;Linux 检查 systemctl status dashboard", "如果 Agent 全部显示离线:检查 ejabberd SSH 连接 — ssh hmo@192.168.1.246 'docker exec ejabberd ejabberdctl connected_users'", "如果 EasyTier/RDP 按钮无反应:检查 xmpp_bot HTTP bridge (:5802) 是否在线 + _BRIDGE_KEY 是否匹配", "如果 OpenCode Go Usage 卡片无数据:检查 usage_collector.py 脚本是否存在 + temp/usage_stats.json 是否生成", "如果 Kanban 无任务:检查 ~/.hermes/kanban.db 是否存在且可读", "如果 spec 弹窗空白:检查 specs/{module}.json 文件是否存在,格式是否正确", "Dashboard 启动失败:检查是否已有实例运行(proc_guard PID 锁)→ 删除 temp/.dashboard.pid" ], "related": "依赖 ejabberd (XMPP 服务器), xmpp_bot (HTTP bridge), usage_collector (用量采集), tests_api (测试运行器), specs/ (双轨文档体系)" }, "ai_spec": { "apis": [ {"method": "GET", "path": "/", "returns": "text/html — dashboard.html 前端页面", "note": "不返回 JSON,直接 serve 模板"}, {"method": "GET", "path": "/api/agents", "returns": "[{id, name, display_name, jid, platform, host, status, status_reason, xmpp_connected, pid, services, message_count_5min, errors, offline_checks, restartable}]", "note": "核心端点 — 三层状态检测 + 自动恢复计数器"}, {"method": "GET", "path": "/api/ejabberd", "returns": "{alive, xmpp_bot_connected, online_jids, bot_jid}", "note": "SSH docker exec ejabberdctl connected_users"}, {"method": "GET", "path": "/api/agents//logs?lines=50", "returns": "{lines:[...]}", "note": "尾读日志文件(最新 50 条)"}, {"method": "POST", "path": "/api/agents//start", "returns": "{ok, started:[...] | service:...}", "note": "启动 Agent — Linux: systemctl start xmpp-{jid}; Windows: subprocess.Popen"}, {"method": "POST", "path": "/api/agents//stop", "returns": "{ok, stopped:[...] | service:...}", "note": "停止 Agent — Linux: systemctl stop; Windows: taskkill /f /pid"}, {"method": "POST", "path": "/api/agents//restart", "returns": "组合 stop 后 2s sleep 再 start", "note": "先调 api_agent_stop → sleep 2s → api_agent_start"}, {"method": "GET", "path": "/api/platform", "returns": "[{id, name, type, desc, status, health_data}]", "note": "Platform 服务状态 — 微信桥接 + 文章处理 (5810) 健康检查"}, {"method": "GET", "path": "/api/service/5810/logs", "returns": "{lines:[...]}", "note": "代理 article-processor 的日志(从 Windows:5810 拉到 dashboard)"}, {"method": "GET", "path": "/api/kanban", "returns": "{tasks:[{id, title, body, status, assignee, created_by, created_at}], db_exists, count}", "note": "读取 ~/.hermes/kanban.db SQLite 数据库"}, {"method": "GET", "path": "/api/git", "returns": "{ok, log:[...], dirty, branch, source, method}", "note": "最近 git 提交历史 + 分支 + 是否有未提交改动 — 用 git log 或降级到 reflog 解析"}, {"method": "GET", "path": "/api/services", "returns": "{services:[{name, port, health, watchdog, pid_lock, type, depends_on}], watched, total}", "note": "服务功能树 + watchdog 覆盖矩阵"}, {"method": "GET", "path": "/api/monitor", "returns": "{tier1, tier2, tasks:[{name, status}], platform}", "note": "Tier1/Tier2 最新检查结果 + 定时任务状态(schtasks on Windows, systemd timer/crontab on Linux)"}, {"method": "GET", "path": "/api/metagrowth", "returns": "{total_fixes, completed_fixes, commit_count, meta_growth_enabled, description}", "note": "元成长回路(Phase 3 规划中)"}, {"method": "GET", "path": "/api/todos", "returns": "{todos:[...], executor_log_tail, count}", "note": "自修复流水线 TODO 执行记录(health_todos.jsonl)+ executor 日志"}, {"method": "GET", "path": "/api/expected", "returns": "{expected:[...], actual:{name:status}}", "note": "期望状态 vs 实际状态矩阵 — 端口扫描 + 进程检测 + 定时任务检查"}, {"method": "GET", "path": "/api/easytier", "returns": "{status:{windows, 246}, virtual_ips, windows_running}", "proxied_to": "xmpp_bot /easytier action=status via _bridge_post"}, {"method": "POST", "path": "/api/easytier/toggle", "body": "{\"action\":\"start|stop\"}", "returns": "{ok, message}", "proxied_to": "xmpp_bot /easytier via _bridge_post"}, {"method": "GET", "path": "/api/rdp", "returns": "{rdp_enabled, tunnel_running, rdp_port, tunnel_port, public_endpoint}", "proxied_to": "xmpp_bot /rdp action=status via _bridge_post"}, {"method": "POST", "path": "/api/rdp/toggle", "body": "{\"action\":\"start|stop\"}", "returns": "{ok, message}", "proxied_to": "xmpp_bot /rdp via _bridge_post"}, {"method": "GET", "path": "/api/usage", "returns": "{ok, accounts:[{key_id, workspace_id, label, rolling, weekly, monthly, last_update, session_expired, error}], last_refresh_iso", "note": "直读 temp/usage_stats.json 缓存"}, {"method": "POST", "path": "/api/usage/refresh", "body": "{}", "returns": "{ok, message}", "note": "触发异步 subprocess 运行 usage_collector.py"}, {"method": "GET", "path": "/api/module-spec/", "returns": "specs/{module}.json 内容", "note": "双轨文档 — 人类 help + AI spec 生成"}, {"method": "GET", "path": "/api/module-specs", "returns": "{specs:[{module, purpose, version}]}", "note": "列出所有可用 spec"}, {"method": "GET", "path": "/api/spec", "returns": "{content, path} — dev-spec.md 全文", "note": "G 规范 tab 内容"}, {"method": "GET", "path": "/api/spec/history", "returns": "{log:[...], count} — git log", "note": "dev-spec.md 的 git 提交历史"}, {"method": "GET", "path": "/api/prd", "returns": "{content, path} — PRD.md 全文", "note": "H 需求 tab 内容"}, {"method": "GET", "path": "/api/prd/history", "returns": "{log:[...], count} — git log", "note": "PRD.md 的 git 提交历史"}, {"method": "GET", "path": "/api/tests", "returns": "tests_api.run_tests() 返回的测试结果", "note": "K 测试 tab — 通过 from tests_api import run_tests 调用"}, {"method": "GET", "path": "/api/health", "returns": "{ok, time, xmpp_bot_alive, ejabberd_alive}", "note": "健康检查端点 — xmpp_bot + ejabberd 双重验证"} ], "dependencies": [ "ejabberd on 192.168.1.246:5222 — 权威在线状态源(SSH docker exec ejabberdctl connected_users)", "xmpp_bot HTTP bridge on Windows 192.168.1.16:5802 — EasyTier/RDP 代理 + /health + /muc", "_bridge_post() + _BRIDGE_KEY = 'xxm_bridge_8f3a2c' — 与 xmpp_bot HTTP bridge 的认证机制", "config/agents.yaml — Agent 实例注册配置(JID/platform/host/services)", "proc_guard.py — PID 锁(防重复启动 dashboard 自身)", "gateway/scripts/specs/*.json — 双轨文档体系(/api/module-spec/ 读取)", "docs/dev-spec.md — G 规范 tab 内容", "docs/PRD.md — H 需求 tab 内容", "tests_api.py — K 测试 tab(from tests_api import run_tests)", "~/.hermes/kanban.db — Kanban 看板数据(SQLite)", "gateway/temp/usage_stats.json — OpenCode Go 用量缓存(usage_collector.py 生成)", "gateway/temp/health_todos.jsonl — 自修复流水线 TODO 记录", "gateway/temp/last_health_check.json — Tier1 健康检查结果", "gateway/temp/last_daily_health.json — Tier2 日报健康检查结果", "gateway/logs/ — 日志聚合目录(_tail_logs 读取)", "gateway/scripts/templates/dashboard.html — 前端 HTML(单文件,内嵌 CSS+JS)", "Python 3.10+ — Flask + yaml + sqlite3 + stdlib only(无额外 pip 依赖)", "SSH 免密登录(Windows: ssh hmo@192.168.1.246) — ejabberdctl 查询需要", "systemctl(Linux) — Agent 启停管理(systemctl start/stop xmpp-{jid})", "nginx on 246 — 公网反向代理(ags.yoin.fun → :5803)" ], "architecture": { "flow": "Dashboard(Flask :5803) → ① ejabberd SSH docker exec → ② xmpp_bot HTTP API :5802 → ③ 本地进程扫描 → 汇总 agent 状态 → JSON API → dashboard.html 前端渲染", "detection_layers": [ "Layer 1 (权威): SSH docker exec ejabberdctl connected_users — 跨平台可信, 查到谁在 XMPP 登录", "Layer 2 (桥接): xmpp_bot /health API — 本地 bot 连接状态(Windows专用)", "Layer 3 (本地): 进程扫描 (ps aux | grep) — 进程存在性检测(Windows专用)", "Fallback: xmpp_bot /muc API — MUC 参与者列表(备选,R01 已知不稳定)" ], "auto_recovery": { "mechanism": "AUTO_RECOVER_THRESHOLD=3 — 连续 3 次检测离线后自动重启", "linux": "sudo systemctl start xmpp-{jid}", "windows": "subprocess.Popen(python script_path) 非阻塞启动", "counter_reset": "一旦状态变回 online,_offline_counter[agent_id] = 0" }, "bridge_proxy": { "mechanism": "_bridge_post(path, payload) → POST to XMPP_BRIDGE_URL with X-Api-Key → xmpp_bot on Windows", "used_by": "EasyTier toggle (/api/easytier/toggle), RDP toggle (/api/rdp/toggle)" }, "pid_lock": "guard('dashboard') — 从 proc_guard 导入,防重复启动。锁文件在 gateway/temp/.dashboard.pid", "usage_auto_timer": "后台线程每 5 分钟自动触发 usage_collector.py,保持用量数据新鲜 + OpenCode Go cookies 保活", "frontend": "dashboard.html 单文件 SPA — CSS 变量暗色主题 + 原生 JS (无框架) + tabs 切换 + 10s 自动轮询 + spec 弹窗 (?/§ 按钮)" }, "constraints": [ "dashboard.html 是单文件 SPA — 不要引入 npm/webpack/vite 等前端框架,保持零依赖", "后端使用 stdlib only — Flask + yaml + sqlite3,不要加额外的 pip 包(除非有充分理由)", "PID 锁机制 — 启动前调用 guard('dashboard'),退出时自动清理锁文件", "跨平台兼容 — sys.platform 检测 win32/linux,SSH vs 直接 docker exec 分支处理", "AGENTSMEETING_ROOT 环境变量 — 在 Linux systemd 部署中覆盖项目路径(/home/hmo/projects/AgentsMeeting)", "XMPP_BRIDGE_URL 默认 http://192.168.1.16:5802 — 可通过环境变量覆盖", "EJABBERD_HOST 默认 192.168.1.246 — 可通过环境变量覆盖", "不要硬编码 workspace ID 或 API keys — 都从 config/accounts.json 或环境变量读取", "SSH 免密登录是 ejabberdctl 查询的前提 — BatchMode=yes, 不提示密码", "usage auto-timer 初始延迟 15s(等 dashboard 稳定后再启动)" ], "must_not": [ "不要修改 wechat_agent.py 的 SCRIPT_NAMES 映射而不更新 auto-recovery 逻辑", "不要在 status_reason 为空时显示空 div — 前端跳过 'OK' 和空字符串", "不要在前端硬编码 agent 信息 — 全部从 /api/agents 动态加载", "不要把 _BRIDGE_KEY 提交到 git — 虽然已在 dashboard.py 中写死,部署前应改为环境变量", "不要在 EasyTier/RDP toggle 端点中直接操作 VPN/RDP — 必须通过 _bridge_post 代理到 xmpp_bot", "不要让 dashboard 的 proc_guard 与 xmpp_bot 的锁冲突 — 各用各的 guard name" ], "related_modules": [ {"module": "agents", "relation": "Agent 注册与生命周期管理 — dashboard 通过 /api/agents 展示 agent 状态,通过 /api/agents//start|stop|restart 管理启停"}, {"module": "api_proxy", "relation": "API 代理服务 (:8787) 与 dashboard 共享 Agent 概念但独立运行"}, {"module": "easytier", "relation": "EasyTier VPN 状态通过 dashboard 的 _bridge_post 代理到 xmpp_bot — 共享相同的 bridge key 和 HTTP 端口"}, {"module": "rdp", "relation": "RDP 远程桌面状态通过 dashboard 的 _bridge_post 代理 — 与 EasyTier 共用同一个 xmpp_bot HTTP bridge"}, {"module": "usage_monitor", "relation": "OpenCode Go 用量监控 — dashboard 通过 /api/usage 读取 usage_collector 生成的缓存 + /api/usage/refresh 触发采集"}, {"module": "health", "relation": "F 健康 tab — dashboard 通过 /api/services + /api/expected + /api/monitor 三个端点汇总健康数据"}, {"module": "kanban", "relation": "Kanban 看板 — dashboard 通过读取 ~/.hermes/kanban.db SQLite 数据库展示任务"}, {"module": "tests", "relation": "K 测试 tab — dashboard 通过 from tests_api import run_tests 调用测试套件"}, {"module": "dev_spec", "relation": "G 规范 tab — dashboard 通过 /api/spec 读取 dev-spec.md 全文展示"}, {"module": "prd", "relation": "H 需求 tab — dashboard 通过 /api/prd 读取 PRD.md 全文展示"} ], "tests": [ {"id": "DB01", "name": "GET / 返回 dashboard.html", "endpoint": "GET / — 期望返回 text/html 200"}, {"id": "DB02", "name": "GET /api/agents 返回 agent 数组", "endpoint": "GET /api/agents — 期望返回数组,每项含 id/status/status_reason/services"}, {"id": "DB03", "name": "POST /api/agents//restart 返回 ok", "endpoint": "POST restart → 期望 ok=true 或符合预期错误"}, {"id": "DB04", "name": "GET /api/health 返回 xmpp_bot_alive + ejabberd_alive", "endpoint": "GET /api/health → 检查字段存在"}, {"id": "DB05", "name": "GET /api/module-spec/dashboard 返回本 spec", "endpoint": "GET /api/module-spec/dashboard → 期望 module='dashboard'"}, {"id": "DB06", "name": "GET /api/module-specs 返回包含 dashboard 的列表", "endpoint": "GET /api/module-specs → specs 数组中应有 module='dashboard'"}, {"id": "DB07", "name": "EasyTier proxy 在 xmpp_bot 不可达时正确返回错误", "endpoint": "GET /api/easytier → 即使 bridge 不可达也应返回 JSON(不 crash)"}, {"id": "DB08", "name": "RDP proxy 在 xmpp_bot 不可达时正确返回错误", "endpoint": "GET /api/rdp → 同上,graceful degradation"}, {"id": "DB09", "name": "Dashboard 不因单个 agent 检测失败而崩溃", "endpoint": "GET /api/agents → 如果 ejabberd SSH 失败,应降级到 muc_participants 或 unknown 状态"} ], "known_issues": [ "R01: MUC join 超时 — MUC 参与者列表不可靠,但 dashboard 只用它做 fallback,所以影响有限", "Windows 端 SSH 依赖免密登录 — 如果 key 丢失或权限不对,ejabberdctl 查询会失败,导致所有 agent 显示 unknown", "EasyTier/RDP 代理依赖 xmpp_bot 在 Windows 上运行 — bot 挂了这些按钮就失效", "dashboard.html 是单文件 ~490 行 JS — 未来扩展可能需要拆分但暂不考虑(保持部署简单)", "auto-recovery 仅支持 Windows 本地进程和 Linux 本地 systemd — Mac/远程 Linux 暂不支持自动恢复", "usage monitor 在 Linux 246 上运行时需要 CDP proxy — 如果 Chrome 没有以 --remote-debugging-port=9222 启动,采集会失败", "Kanban 看板直接读 ~/.hermes/kanban.db — 如果 Hermes 升级后 DB schema 变更,需要同步更新 SELECT 语句" ], "related_files": [ "gateway/scripts/dashboard.py — 本文件(Flask 后端,1377行)", "gateway/scripts/templates/dashboard.html — 前端 UI(单文件 SPA,490行)", "gateway/scripts/specs/dashboard.json — 本 spec 文件", "gateway/scripts/proc_guard.py — PID 锁(dashboard 启动时调用 guard('dashboard'))", "gateway/scripts/tests_api.py — K 测试 tab 调用的测试套件", "gateway/scripts/usage_collector.py — OpenCode Go 用量采集脚本(dashboard auto-timer 触发)", "config/agents.yaml — Agent 实例注册配置", "docs/dev-spec.md — G 规范 tab 文档", "docs/PRD.md — H 需求 tab 文档", "~/.hermes/kanban.db — Kanban 看板数据", "gateway/temp/ — 运行时临时文件(health_todos.jsonl, last_health_check.json, usage_stats.json, .dashboard.pid 等)" ] } }