Files
AgentsMeeting/gateway/scripts/specs/dashboard.json
T

193 lines
20 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"module": "dashboard",
"version": "2.1",
"purpose": "AgentsMeeting 管理门户 — Flask app on :5803。监控所有 Agent 的在线状态(跨平台 SSH ejabberdctl + xmpp_bot HTTP API + 本地进程检测)、提供 Web UItabs: 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 是所有 Agentxxm/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 (:5807) + EasyTier/RDP 控制代理", "note": "dashboard 本地运行用于开发和测试"},
{"name": "ejabberd", "device": "246 Docker", "role": "XMPP 服务器 — 权威在线状态源", "note": "通过 SSH docker exec ejabberdctl connected_users 查询(Windows)或直接 docker execLinux"},
{"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 tabPlatform 服务状态 + 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 5803Linux 检查 systemctl status dashboard",
"如果 Agent 全部显示离线:检查 ejabberd SSH 连接 — ssh hmo@192.168.1.246 'docker exec ejabberd ejabberdctl connected_users'",
"如果 EasyTier/RDP 按钮无反应:检查 xmpp_bot HTTP bridge (:5807) 是否在线 + _BRIDGE_KEY 是否匹配 + XMPP_BRIDGE_URL 是否指向 5807",
"如果 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/<agent_id>/logs?lines=50", "returns": "{lines:[...]}", "note": "尾读日志文件(最新 50 条)"},
{"method": "POST", "path": "/api/agents/<agent_id>/start", "returns": "{ok, started:[...] | service:...}", "note": "启动 Agent — Linux: systemctl start xmpp-{jid}; Windows: subprocess.Popen"},
{"method": "POST", "path": "/api/agents/<agent_id>/stop", "returns": "{ok, stopped:[...] | service:...}", "note": "停止 Agent — Linux: systemctl stop; Windows: taskkill /f /pid"},
{"method": "POST", "path": "/api/agents/<agent_id>/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/<module>", "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/autoheal", "returns": "{time, dry_run, total_expected, anomalies_found, actions_taken[]}", "note": "读取 temp/last_auto_heal.jsonauto_heal.py 每次运行写入的摘要)"},
{"method": "GET", "path": "/api/keys", "returns": "{ok, keys:[{key_id, workspace_id, label, masked_key, ...}], usage:{...}}", "note": "Key Registry — 列出所有 API keymasked+ 用量数据(合并 accounts.json 与 usage_stats.json"},
{"method": "GET", "path": "/api/wechat/status", "returns": "{online, message, port_ok, qr_url, qr_timestamp, session_age_hours, last_ok_at, last_err_at}", "note": "WeChat Bridge 登录状态 + QR 码(TCP check port 3001"},
{"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:5807 — 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/<module> 读取)",
"docs/dev-spec.md — G 规范 tab 内容",
"docs/PRD.md — H 需求 tab 内容",
"tests_api.py — K 测试 tabfrom 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 查询需要",
"systemctlLinux — 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 :5807 → ③ 本地进程扫描 → 汇总 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/linuxSSH vs 直接 docker exec 分支处理",
"AGENTSMEETING_ROOT 环境变量 — 在 Linux systemd 部署中覆盖项目路径(/home/hmo/projects/AgentsMeeting",
"XMPP_BRIDGE_URL 默认 http://192.168.1.16:5807 — 可通过环境变量覆盖(246 systemd 已设 5807",
"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/<id>/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/<id>/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(单文件 SPA490行)",
"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 等)"
]
}
}