fix: remove stale Loading span in Infrastructure tab; add per-service ?/§ for Platform (WeChat Bridge, API Proxy, article_processor) and Ejabberd with dedicated spec files
This commit is contained in:
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"module": "api_proxy",
|
||||||
|
"version": "1.0",
|
||||||
|
"purpose": "API Proxy 运行在 Windows (192.168.1.16:8787),为 VolcEngine(火山引擎)等外部 API 提供代理转发、重试和降级能力。",
|
||||||
|
"ui_location": "Infrastructure tab → Platform → API Proxy",
|
||||||
|
|
||||||
|
"human_help": {
|
||||||
|
"title": "API Proxy(API 代理 :8787)",
|
||||||
|
"description": [
|
||||||
|
"API Proxy 是一个轻量级反向代理,专门用于转发 VolcEngine(火山引擎)DeepSeek API 的请求。",
|
||||||
|
"主要功能:",
|
||||||
|
"• 请求代理——将内部请求转发到 VolcEngine 端点",
|
||||||
|
"• 自动重试——遇到网络抖动时自动重试",
|
||||||
|
"• 失败降级——主 API 不可用时自动切换到备用端点",
|
||||||
|
"简单说:它就是 AI API 的'中间人',保证请求能发出去、失败能重来。"
|
||||||
|
],
|
||||||
|
"usage": [
|
||||||
|
"监控状态:在 Infrastructure → Platform 下查看状态指示灯",
|
||||||
|
"如果状态为 stopped:AI 请求会直接失败",
|
||||||
|
"需要先确认 WeChat Bridge 正常运行"
|
||||||
|
],
|
||||||
|
"troubleshooting": [
|
||||||
|
"如果 API Proxy 停止:检查 Windows 上 api_proxy.py 进程是否在运行",
|
||||||
|
"排障命令:检查端口 8787 是否监听",
|
||||||
|
"重试策略:超时 5s,最多重试 2 次,失败后尝试备用端点"
|
||||||
|
],
|
||||||
|
"related": "WeChat Bridge(依赖 API Proxy 访问 AI)、Ejabberd(底层通信)"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ai_spec": {
|
||||||
|
"apis": [
|
||||||
|
{"method": "POST", "path": "/v1/chat/completions", "returns": "OpenAI 兼容的 chat completion 响应"},
|
||||||
|
{"method": "GET", "path": "/health", "returns": "{ok, status}"}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"依赖 VolcEngine(火山引擎)API 可用",
|
||||||
|
"依赖 Windows 网络连通性"
|
||||||
|
],
|
||||||
|
"constraints": [
|
||||||
|
"仅用于 AI 模型 API 调用,不代理其他流量",
|
||||||
|
"重试策略:5s 超时 + 2 次重试 + 备用端点自动切换",
|
||||||
|
"端口 8787 在 Windows 上监听"
|
||||||
|
],
|
||||||
|
"related_files": [
|
||||||
|
"gateway/scripts/api_proxy.py — 实际运行脚本",
|
||||||
|
"gateway/scripts/templates/dashboard.html — fI() 状态展示",
|
||||||
|
"gateway/scripts/specs/api_proxy.json — 本 spec 文件"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"module": "article_processor",
|
||||||
|
"version": "1.0",
|
||||||
|
"purpose": "文章抓取服务运行在 Windows (192.168.1.16:5810),负责抓取微信公众号全文链接并转换为纯文本,支持 OCR 图片识别。",
|
||||||
|
"ui_location": "Infrastructure tab → Platform → 文章抓取服务 (5810)",
|
||||||
|
|
||||||
|
"human_help": {
|
||||||
|
"title": "文章抓取服务 (:5810)",
|
||||||
|
"description": [
|
||||||
|
"这是整个系统的'阅读器'。当莫荷或小小莫需要阅读一个微信公众号文章链接时,由这个服务负责:",
|
||||||
|
"① 用 Chrome CDP 打开链接",
|
||||||
|
"② 等页面加载完成",
|
||||||
|
"③ 提取全文内容转为 Markdown",
|
||||||
|
"④ 如果包含图片,调用 GLM-OCR 识别图片文字",
|
||||||
|
"⑤ 返回结构化内容给请求方",
|
||||||
|
"依赖微信的 CDP session:如果微信未登录,抓取会失败('等待登录超时')。"
|
||||||
|
],
|
||||||
|
"usage": [
|
||||||
|
"监控状态:Infrastructure → Platform 下查看状态和最近抓取信息",
|
||||||
|
"如果 health_data 中有 error:表示最近一次抓取失败,查看 error 内容",
|
||||||
|
"点击服务旁的 § 查看 AI 接口详情"
|
||||||
|
],
|
||||||
|
"troubleshooting": [
|
||||||
|
"如果状态 stopped:检查 Windows 上 article_processor.py 进程",
|
||||||
|
"如果抓取失败返回 '等待登录超时':微信 CDP session 已过期,需要重新登录",
|
||||||
|
"如果图片 OCR 失败:检查 GLM-OCR 服务是否可用",
|
||||||
|
"日志位置:gateway/logs/article_processor.log"
|
||||||
|
],
|
||||||
|
"related": "WeChat Bridge(依赖文章抓取服务处理微信中的链接)"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ai_spec": {
|
||||||
|
"apis": [
|
||||||
|
{"method": "GET", "path": "/health", "returns": "{ok, service, port, ocr_model, status}", "desc": "服务状态 + OCR 模型信息"},
|
||||||
|
{"method": "GET", "path": "/logs?lines=N", "returns": "{ok, lines[]}", "desc": "最近 N 行日志"},
|
||||||
|
{"method": "POST", "path": "/fetch", "body": "{\"url\":\"...\"}", "returns": "{ok, title, content, images[]}"}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"依赖 Chrome CDP session(微信登录态)",
|
||||||
|
"依赖 GLM-OCR 服务识别图片文字",
|
||||||
|
"依赖网络连通性访问微信公众号"
|
||||||
|
],
|
||||||
|
"constraints": [
|
||||||
|
"CDP session 过期后需要重新登录微信才能恢复",
|
||||||
|
"OCR 使用 GLM-OCR-8bit 模型",
|
||||||
|
"Linux 上访问 Windows 的此服务时需要 EasyTier VPN 连通"
|
||||||
|
],
|
||||||
|
"related_files": [
|
||||||
|
"gateway/scripts/article_processor.py — 实际运行脚本",
|
||||||
|
"gateway/scripts/templates/dashboard.html — fI() 状态展示(health_data)",
|
||||||
|
"gateway/scripts/specs/article_processor.json — 本 spec 文件"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"module": "ejabberd",
|
||||||
|
"version": "1.0",
|
||||||
|
"purpose": "Ejabberd XMPP 服务器运行在 Linux (246),是整个 Agent 通信系统的底层消息总线。所有的 Agent(xxm、mohe、zhiwei、xiaoguo)都通过它建立群聊(MUC)和私聊。",
|
||||||
|
"ui_location": "Infrastructure tab → Ejabberd",
|
||||||
|
|
||||||
|
"human_help": {
|
||||||
|
"title": "Ejabberd(XMPP 服务器)",
|
||||||
|
"description": [
|
||||||
|
"Ejabberd 是一个开源的 XMPP(Extensible Messaging and Presence Protocol)服务器。在这个系统中,它是所有 Agent 之间通信的底层基础设施。",
|
||||||
|
"每个 Agent 有一个 JID(Jabber ID,类似邮箱地址),例如 xxm@yoin.fun。Agent 们在 coregroup@conference.yoin.fun 群聊中收发消息。",
|
||||||
|
"Ejabberd 如果挂掉,所有 Agent 之间的消息都无法传递。dashboard 本身、WeChat Bridge、Hermes API 等都不依赖 Ejabberd(它们走 HTTP)。"
|
||||||
|
],
|
||||||
|
"usage": [
|
||||||
|
"监控状态:Infrastructure → Ejabberd 显示 ALIVE 或 DOWN",
|
||||||
|
"同时能看到当前在线的 JID 列表(online_jids)",
|
||||||
|
"如果显示 DOWN:必须立即登录 Linux 恢复"
|
||||||
|
],
|
||||||
|
"troubleshooting": [
|
||||||
|
"Ejabberd 挂掉时:ssh 到 246,运行 systemctl status ejabberd 查看状态",
|
||||||
|
"重启:sudo systemctl restart ejabberd",
|
||||||
|
"如果 Agent 连不上:检查 ejabberd 配置是否有对应的 JID 注册",
|
||||||
|
"端口 3021:客户端连接(C2S),开启 STARTTLS"
|
||||||
|
],
|
||||||
|
"related": "F 健康 tab 查看 XMPP bot 连接状态、G 规范查看 Agent 通信协议"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ai_spec": {
|
||||||
|
"apis": [
|
||||||
|
{"method": "GET", "path": "/api/ejabberd", "returns": "{alive, xmpp_bot_connected, online_jids[], bot_jid}", "desc": "Ejabberd 存活状态 + 在线 JID 列表"}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"Ejabberd 本身不依赖其他服务",
|
||||||
|
"Agent 们依赖 Ejabberd 收发消息"
|
||||||
|
],
|
||||||
|
"constraints": [
|
||||||
|
"Dashboard 通过 HTTP API 检测 Ejabberd 健康(非 XMPP 协议直连)",
|
||||||
|
"online_jids 从 ejabberd API 获取(mod_admin_extra 需要开启)",
|
||||||
|
"xmpp_bot_connected 标识是否有至少一个 xmpp_agent_core 实例在线"
|
||||||
|
],
|
||||||
|
"related_files": [
|
||||||
|
"gateway/scripts/dashboard.py — /api/ejabberd 端点",
|
||||||
|
"gateway/scripts/templates/dashboard.html — fI() 状态展示",
|
||||||
|
"gateway/scripts/specs/ejabberd.json — 本 spec 文件"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,70 +1,67 @@
|
|||||||
{
|
{
|
||||||
"module": "infra",
|
"module": "infra",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"purpose": "Dashboard 'Infrastructure' tab 展示基础设施状态:Platform 进程、Ejabberd XMPP 服务、EasyTier VPN、RDP 远程桌面隧道、OpenCode Go 用量监控。",
|
"purpose": "Platform 服务总览:展示 Windows 上运行的三个核心服务(WeChat Bridge、API Proxy、文章抓取服务)的运行状态,每个服务可通过其旁边的 ?/§ 查看详细文档。",
|
||||||
"ui_location": "主菜单 → Infrastructure tab",
|
"ui_location": "Infrastructure tab → Platform",
|
||||||
|
|
||||||
"human_help": {
|
"human_help": {
|
||||||
"title": "Infrastructure 总览",
|
"title": "Platform 服务概览",
|
||||||
"description": [
|
"description": [
|
||||||
"本页面展示系统基础设施的运行状态,分为多个区域:",
|
"Platform 区域展示在 Windows (192.168.1.16) 上运行的三个核心基础设施服务:",
|
||||||
"• Platform — Windows/Linux 平台基础进程状态",
|
"• WeChat Bridge (:5801) — 微信消息桥接,连接微信 ↔ Hermes AI",
|
||||||
"• Ejabberd — XMPP 服务器心跳",
|
"• API Proxy (:8787) — API 代理,转发 VolcEngine DeepSeek 请求",
|
||||||
"• EasyTier VPN — P2P VPN 内网开关(连接两台机器)",
|
"• 文章抓取服务 (:5810) — 微信公众号全文抓取 + OCR",
|
||||||
"• RDP Remote Desktop — SSH 反向隧道远程桌面",
|
"",
|
||||||
"• OpenCode Go Usage — 四个订阅账号的 API 用量监控"
|
"每个服务右侧有单独的 ?(使用说明)和 §(AI Spec)按钮。",
|
||||||
|
"点击可查看该服务的详细文档、API 端点和排障指南。",
|
||||||
|
"",
|
||||||
|
"其他基础设施部分(EasyTier / RDP / Usage / Ejabberd)各自独立,在本 tab 下方。"
|
||||||
],
|
],
|
||||||
"usage": [
|
"usage": [
|
||||||
"1. 检查 Platform 和 Ejabberd 的基础运行状态",
|
"状态指示:绿色圆点 = 运行中,红色圆点 = 已停止",
|
||||||
"2. EasyTier: 先 Turn On 建立 VPN 连接,确认 Connected 后再操作 RDP",
|
"点击服务旁的 ? 查看人类可读的说明文档",
|
||||||
"3. RDP: Enable 启动 SSH 反向隧道,Disable 关闭",
|
"点击服务旁的 § 查看 AI 接口文档(API、依赖、约束)",
|
||||||
"4. Usage: 查看各账号用量,点击 Refresh Now 手动采集最新数据"
|
"如果服务 stopped:检查 Windows 上对应的进程是否在运行"
|
||||||
],
|
],
|
||||||
"troubleshooting": [
|
"troubleshooting": [
|
||||||
"EasyTier 和 RDP 有依赖关系 — RDP 需 EasyTier 先 Connected",
|
"如果所有服务都 stopped:Windows 可能关机了,或 wechat_agent 没启动",
|
||||||
"Usage 数据自动每 30min 采集一次,也可手动 Refresh",
|
"如果单个服务 stopped:检查该服务的具体进程",
|
||||||
"如果某个区域一直 loading:检查对应后端 API 是否正常"
|
"Dashboard 每 5-10s 自动刷新 Platform 状态"
|
||||||
],
|
],
|
||||||
"related": "F 健康 tab 查看服务级健康状态"
|
"related": "Ejabberd(底层通信总线)、F 健康 tab(系统整体健康)"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ai_spec": {
|
"ai_spec": {
|
||||||
"apis": [
|
"apis": [
|
||||||
{"method": "GET", "path": "/api/platform", "returns": "[{name,status}] — 平台进程状态"},
|
{"method": "GET", "path": "/api/platform", "returns": "[{id,name,type,desc,status,health_data}] — 三个 Platform 服务的状态列表"}
|
||||||
{"method": "GET", "path": "/api/ejabberd", "returns": "{alive, xmpp_bot_connected} — XMPP 服务器心跳"},
|
|
||||||
{"method": "GET", "path": "/api/easytier", "returns": "{status:{windows,246}, virtual_ips:{windows,246}}"},
|
|
||||||
{"method": "POST", "path": "/api/easytier/toggle", "body": "{action:start|stop}"},
|
|
||||||
{"method": "GET", "path": "/api/rdp", "returns": "{rdp_enabled, tunnel_running, public_endpoint}"},
|
|
||||||
{"method": "POST", "path": "/api/rdp/toggle", "body": "{action:start|stop}"},
|
|
||||||
{"method": "GET", "path": "/api/usage", "returns": "{ok, accounts[{name,usage,limit}], last_refresh}"},
|
|
||||||
{"method": "POST", "path": "/api/usage/refresh", "returns": "{ok}"}
|
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"EasyTier/RDP 依赖 xmpp_bot 的 HTTP bridge (Windows:5807)",
|
"Platform 服务运行在 Windows (192.168.1.16),Dashboard (Linux 246) 通过网络检测",
|
||||||
"Usage 依赖 CDP proxy 在 Chrome tab 内执行 fetch",
|
"WeChat Bridge 使用 health_url = http://192.168.1.16:5801/health",
|
||||||
"Platform 状态通过 subprocess 本地检测"
|
"API Proxy 使用 port check (8787)",
|
||||||
|
"文章抓取服务使用 health_url = http://192.168.1.16:5810/health"
|
||||||
],
|
],
|
||||||
"constraints": [
|
"constraints": [
|
||||||
"fI() 使用 create-once/update-state pattern — DOM 元素只创建一次,后续只更新 class/text",
|
"Dashboard 从 246 通过 HTTP/端口扫描检测 Windows 服务",
|
||||||
"Platform 和 Ejabberd 部分在 stable wrapper (<div id=infra-pe>) 中全量重渲染",
|
"如果 246 无法连通 16 上的端口,所有服务显示 stopped",
|
||||||
"EasyTier/RDP/Usage section 各自独立创建,互不影响",
|
"Platform 和 Ejabberd 内容在 infra-pe wrapper 中全量重渲染"
|
||||||
"Usage 数据采集后 10-15s 自动刷新"
|
|
||||||
],
|
|
||||||
"must_not": [
|
|
||||||
"不要把 fI() 改成全量重渲染 — 会破坏 create-once pattern 导致 EasyTier/RDP 元素闪烁",
|
|
||||||
"不要删除 infra-pe wrapper — Platform 和 Ejabberd 内容全量重渲染,其他 section 增量更新"
|
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
{"id": "IN01", "name": "Platform 状态正常返回", "endpoint": "GET /api/platform"},
|
{"id": "IN01", "name": "Platform 状态正常返回", "endpoint": "GET /api/platform"}
|
||||||
{"id": "IN02", "name": "Ejabberd 心跳正常", "endpoint": "GET /api/ejabberd"},
|
],
|
||||||
{"id": "IN03", "name": "EasyTier toggle 操作正常", "endpoint": "POST /api/easytier/toggle"},
|
"related_modules": [
|
||||||
{"id": "IN04", "name": "RDP toggle 操作正常", "endpoint": "POST /api/rdp/toggle"},
|
{"module": "wechat_bridge", "relation": "Platform 核心服务 — 微信消息桥接"},
|
||||||
{"id": "IN05", "name": "Usage 数据正常采集和展示", "endpoint": "GET /api/usage"}
|
{"module": "api_proxy", "relation": "Platform 核心服务 — API 代理转发"},
|
||||||
|
{"module": "article_processor", "relation": "Platform 核心服务 — 文章抓取 + OCR"},
|
||||||
|
{"module": "ejabberd", "relation": "XMPP 通信总线(独立于 Platform)"}
|
||||||
],
|
],
|
||||||
"related_files": [
|
"related_files": [
|
||||||
"gateway/scripts/templates/dashboard.html — fI() 渲染函数",
|
"gateway/scripts/templates/dashboard.html — fI() 渲染函数",
|
||||||
"gateway/scripts/dashboard.py — 所有 /api/infra* 端点",
|
"gateway/scripts/dashboard.py — /api/platform 端点",
|
||||||
"gateway/scripts/specs/infra.json — 本 spec 文件",
|
"gateway/scripts/specs/infra.json — 本 spec 文件",
|
||||||
|
"gateway/scripts/specs/wechat_bridge.json — WeChat Bridge 详细 spec",
|
||||||
|
"gateway/scripts/specs/api_proxy.json — API Proxy 详细 spec",
|
||||||
|
"gateway/scripts/specs/article_processor.json — 文章抓取服务详细 spec",
|
||||||
"gateway/scripts/specs/easytier.json — EasyTier 详细 spec",
|
"gateway/scripts/specs/easytier.json — EasyTier 详细 spec",
|
||||||
"gateway/scripts/specs/rdp.json — RDP 详细 spec",
|
"gateway/scripts/specs/rdp.json — RDP 详细 spec",
|
||||||
"gateway/scripts/specs/usage_monitor.json — Usage monitor 详细 spec"
|
"gateway/scripts/specs/usage_monitor.json — Usage monitor 详细 spec"
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"module": "wechat_bridge",
|
||||||
|
"version": "1.0",
|
||||||
|
"purpose": "WeChat Bridge 服务运行在 Windows (192.168.1.16:5801),负责将微信消息桥接到 Hermes Gateway (246:8642),实现老爸在微信上与莫荷的双向通信。",
|
||||||
|
"ui_location": "Infrastructure tab → Platform → WeChat Bridge",
|
||||||
|
|
||||||
|
"human_help": {
|
||||||
|
"title": "WeChat Bridge(微信桥接 :5801)",
|
||||||
|
"description": [
|
||||||
|
"这是整个系统的核心通道。部署在 Windows 上,接收微信消息后通过 HTTP POST 转发到 Linux (246) 上的 Hermes API,处理完成后再通过 wxhelper 将回复发回微信。",
|
||||||
|
"简单说:老爸在微信上发消息 → WeChat Bridge 接收 → 传给 Hermes AI 处理 → 回复发回微信。",
|
||||||
|
"它是一切的起点——没了它,微信通道就断了。"
|
||||||
|
],
|
||||||
|
"usage": [
|
||||||
|
"监控状态:在 Infrastructure → Platform 下查看状态指示灯(绿=运行,红=停止)",
|
||||||
|
"如果状态为 stopped:需要检查 Windows 上的 wechat_agent.py 进程",
|
||||||
|
"启动脚本:start-bot-server.bat(自动启动 opencode serve + wechat_agent)"
|
||||||
|
],
|
||||||
|
"troubleshooting": [
|
||||||
|
"如果 WeChat Bridge 停止:先检查 Windows 上 wxhelper 是否注入成功",
|
||||||
|
"如果 Hermes 不回复:检查 Linux 上 Hermes Gateway (:8642) 是否存活",
|
||||||
|
"如果消息延迟:消息走 HTTP 桥,网络延迟通常 < 2s",
|
||||||
|
"如果启动了但状态还是 stopped:页面每 5-10s 自动刷新,等一会"
|
||||||
|
],
|
||||||
|
"related": "Ejabberd(XMPP 底层通信)、F 健康 tab(系统整体健康)"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ai_spec": {
|
||||||
|
"apis": [
|
||||||
|
{"method": "GET", "path": "/health", "returns": "{ok, status} — 服务健康检查"}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"依赖 wxhelper DLL 注入微信 3.9.5.81",
|
||||||
|
"依赖 Hermes API (:8642) 存活才能处理消息",
|
||||||
|
"依赖 EasyTier VPN 打通网络(Windows ↔ Linux)"
|
||||||
|
],
|
||||||
|
"constraints": [
|
||||||
|
"只支持 3.9.5.81 x64 微信版本",
|
||||||
|
"注入 wxhelper 后如果微信重启需重新注入",
|
||||||
|
"Python 请用 3.10(Miniconda3 3.13 的 encodings 模块损坏)"
|
||||||
|
],
|
||||||
|
"related_files": [
|
||||||
|
"gateway/scripts/wechat_agent.py — 实际运行脚本",
|
||||||
|
"gateway/scripts/templates/dashboard.html — fI() 状态展示",
|
||||||
|
"gateway/scripts/specs/wechat_bridge.json — 本 spec 文件"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,8 @@ h1{font-size:32px;font-weight:600;color:var(--accent);margin-bottom:4px}.subtitl
|
|||||||
.st{display:inline-flex;align-items:center;gap:4px;padding:4px 12px;border-radius:12px;font-size:19px;background:#0d1117;border:1px solid var(--border)}
|
.st{display:inline-flex;align-items:center;gap:4px;padding:4px 12px;border-radius:12px;font-size:19px;background:#0d1117;border:1px solid var(--border)}
|
||||||
.st .d{width:6px;height:6px;border-radius:50%}.st .d.running{background:var(--green)}.st .d.stopped{background:var(--dim)}
|
.st .d{width:6px;height:6px;border-radius:50%}.st .d.running{background:var(--green)}.st .d.stopped{background:var(--dim)}
|
||||||
.st .p{color:var(--accent);font-family:monospace;font-size:18px;margin-left:4px}
|
.st .p{color:var(--accent);font-family:monospace;font-size:18px;margin-left:4px}
|
||||||
|
.i{display:flex;align-items:center;gap:6px;margin:4px 0}
|
||||||
|
.sn{flex:1;min-width:0}
|
||||||
.lp{display:none;border-top:1px solid var(--border);padding:12px 16px;background:#0a0e14}
|
.lp{display:none;border-top:1px solid var(--border);padding:12px 16px;background:#0a0e14}
|
||||||
.lp.open{display:block}.lh{font-size:18px;color:var(--dim);margin-bottom:8px;display:flex;justify-content:space-between}
|
.lp.open{display:block}.lh{font-size:18px;color:var(--dim);margin-bottom:8px;display:flex;justify-content:space-between}
|
||||||
.lc{font:11px/1.6 "Cascadia Code",Consolas,monospace;color:var(--dim);max-height:300px;overflow-y:auto;background:#06080c;border:1px solid var(--border);border-radius:6px;padding:10px;white-space:pre-wrap}
|
.lc{font:11px/1.6 "Cascadia Code",Consolas,monospace;color:var(--dim);max-height:300px;overflow-y:auto;background:#06080c;border:1px solid var(--border);border-radius:6px;padding:10px;white-space:pre-wrap}
|
||||||
@@ -271,17 +273,18 @@ if(es)es.onchange=fK;if(ea)ea.onchange=fK;}catch(e){}}
|
|||||||
async function fI(){
|
async function fI(){
|
||||||
try{
|
try{
|
||||||
var ci=document.getElementById('ct-infra');if(!ci)return;
|
var ci=document.getElementById('ct-infra');if(!ci)return;
|
||||||
|
var ls=ci.querySelector('span.dim');if(ls)ls.remove();
|
||||||
/* --- Stable wrapper for Platform+Ejabberd (rebuilt each cycle, no flicker since no state to preserve) --- */
|
/* --- Stable wrapper for Platform+Ejabberd (rebuilt each cycle, no flicker since no state to preserve) --- */
|
||||||
var pe=document.getElementById('infra-pe');
|
var pe=document.getElementById('infra-pe');
|
||||||
if(!pe){pe=document.createElement('div');pe.id='infra-pe';ci.appendChild(pe);}
|
if(!pe){pe=document.createElement('div');pe.id='infra-pe';ci.appendChild(pe);}
|
||||||
var h='';
|
var h='';
|
||||||
try{var r=await fetch('/api/platform'),d=await r.json();
|
try{var r=await fetch('/api/platform'),d=await r.json();
|
||||||
h+='<div class="ps"><h2>Platform<span class="help-btn" onclick="showModuleHelp(\'infra\',\'human\')" title="使用说明">?</span><span class="help-btn ai" onclick="showModuleHelp(\'infra\',\'ai\')" title="AI Spec">§</span></h2><div class="psv">';
|
h+='<div class="ps"><h2>Platform<span class="help-btn" onclick="showModuleHelp(\'infra\',\'human\')" title="使用说明">?</span><span class="help-btn ai" onclick="showModuleHelp(\'infra\',\'ai\')" title="AI Spec">§</span></h2><div class="psv">';
|
||||||
for(var i=0;i<d.length;i++){h+='<div class="i"><span class="d '+(d[i].status==='running'?'ok':'stopped')+'"></span>'+esc(d[i].name)+'</div>';}
|
for(var i=0;i<d.length;i++){var s=d[i];h+='<div class="i"><span class="d '+(s.status==='running'?'ok':'stopped')+'"></span><span class="sn">'+esc(s.name)+'</span><span class="help-btn" onclick="showModuleHelp(\''+s.id+'\',\'human\')" title="使用说明">?</span><span class="help-btn ai" onclick="showModuleHelp(\''+s.id+'\',\'ai\')" title="AI Spec">§</span></div>';}
|
||||||
h+='</div></div>';
|
h+='</div></div>';
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
try{var r2=await fetch('/api/ejabberd'),e=await r2.json();var ea=e.alive||e.xmpp_bot_connected;
|
try{var r2=await fetch('/api/ejabberd'),e=await r2.json();var ea=e.alive||e.xmpp_bot_connected;
|
||||||
h+='<div class="ps"><h2>Ejabberd</h2><div class="psv"><div class="i"><span class="d '+(ea?'ok':'stopped')+'"></span>'+(ea?'ALIVE':'DOWN')+'</div></div></div>';
|
h+='<div class="ps"><h2>Ejabberd<span class="help-btn" onclick="showModuleHelp(\'ejabberd\',\'human\')" title="使用说明">?</span><span class="help-btn ai" onclick="showModuleHelp(\'ejabberd\',\'ai\')" title="AI Spec">§</span></h2><div class="psv"><div class="i"><span class="d '+(ea?'ok':'stopped')+'"></span>'+(ea?'ALIVE':'DOWN')+'</div></div></div>';
|
||||||
}catch(e2){}
|
}catch(e2){}
|
||||||
pe.innerHTML=h;
|
pe.innerHTML=h;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user