fix: 第一轮修复 - 记忆管理/IoT操控/历史消息持久化/动作消息/链路优化/安全配置

- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复)
- 修复IoT子会话工具调用链路日志缺失
- 新增最终审查子会话(review_provider) 支持消息格式解析拆分
- 实现历史消息持久化(后端存储+前端分页加载)
- 前端新增动作消息(ActionMessage)类型和渲染
- 优化对话链路速度(非阻塞子会话+快速问候通道)
- JWT密钥环境变量化(无默认值启动panic)
- Token自动刷新机制(401拦截器+refresh接口)
- WebSocket指数退避重连(jitter+最大10次)
- localStorage清理一致性(cyrene_前缀+版本检查)
- IoT环境变量统一为IOT_SERVICE_URL
This commit is contained in:
2026-05-21 23:10:07 +08:00
parent 8b7d4ec19a
commit a058b0ab8e
53 changed files with 5535 additions and 241 deletions
+134
View File
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""CDP 前端验证:截图 + 控制台检查 + 页面状态"""
import json, time, base64, urllib.request, os
# Step 1: 导航到前端页面 (PUT /json/new)
print("=== CDP 前端验证 ===")
print("Navigating to page...")
req = urllib.request.Request("http://127.0.0.1:9225/json/new?url=http://localhost:5199/", method="PUT")
try:
resp = urllib.request.urlopen(req, timeout=10)
page_data = json.loads(resp.read())
page_id = page_data.get("id","")
ws_url = page_data.get("webSocketDebuggerUrl","")
print(f" Page: id={page_id} title={page_data.get('title','')[:80]}")
print(f" WS_URL: {ws_url[:80]}")
except Exception as e:
print(f" Navigation failed: {e}")
import sys; sys.exit(1)
# Step 2: 连接到页面 WebSocket
from websocket import create_connection
ws = create_connection(ws_url, timeout=10)
print(" Connected to page WebSocket")
def cdp(method, params=None, msg_id=1):
payload = json.dumps({"id": msg_id, "method": method, "params": params or {}})
ws.send(payload)
def recv_msgs(timeout=3):
ws.settimeout(timeout)
msgs = []
try:
while True:
msgs.append(ws.recv())
except:
pass
return msgs
def find_result(msgs, msg_id):
for m in msgs:
try:
d = json.loads(m)
if d.get("id") == msg_id:
return d.get("result",{})
except:
pass
return None
# Step 3: 启用 domains
print("\nEnabling domains...")
cdp("Runtime.enable", {}, 1)
cdp("Page.enable", {}, 2)
cdp("Log.enable", {}, 3)
recv_msgs(2)
print(" Domains enabled")
# Step 4: 等待页面完全加载后截图
print("\nWaiting for page to load...")
time.sleep(3)
cdp("Page.captureScreenshot", {"format": "png"}, 10)
msgs = recv_msgs(5)
r = find_result(msgs, 10)
if r and r.get("data"):
img = base64.b64decode(r["data"])
with open("/tmp/cyrene_screenshot_round12.png", "wb") as f:
f.write(img)
print(f" Screenshot saved: {len(img)} bytes")
else:
print(f" Screenshot failed: {str(r)[:200]}")
# Step 5: 获取控制台日志
print("\nConsole messages (Log.entryAdded):")
ws.settimeout(2)
logs = []
for i in range(15):
try:
data = ws.recv()
d = json.loads(data)
if d.get("method") == "Log.entryAdded":
entry = d["params"]["entry"]
level = entry.get("level","log")
text = entry.get("text","")
url = entry.get("url","")
logs.append(f" [{level}] {text[:200]} ({url})")
elif d.get("method") == "Runtime.exceptionThrown":
exc = d["params"].get("exceptionDetails",{})
logs.append(f" [EXCEPTION] {exc.get('text','')}")
except:
break
if logs:
for l in logs:
print(l)
else:
print(" (none)")
# Step 6: 页面状态检查
print("\nPage state:")
checks = [
("title", "document.title"),
("readyState", "document.readyState"),
("root exists?", "document.getElementById('root') ? 'yes' : 'no'"),
("body children", "document.body ? document.body.children.length : -1"),
("login form?", "document.querySelector('form') ? 'yes' : 'no'"),
("all text content (first 300)", "document.body ? (document.body.innerText || '').substring(0, 300) : 'no body'"),
]
for idx, (label, expr) in enumerate(checks):
cdp("Runtime.evaluate", {"expression": expr, "returnByValue": True}, 100 + idx)
msgs = recv_msgs(2)
r = find_result(msgs, 100 + idx)
val = r.get("result",{}).get("value","?") if r else "?"
print(f" {label}: {val}")
# Step 7: 前端 JS 错误检测
print("\nJS Errors (Runtime.exceptionThrown):")
ws.settimeout(1)
errors = []
for i in range(5):
try:
d = json.loads(ws.recv())
if d.get("method") == "Runtime.exceptionThrown":
exc = d["params"]["exceptionDetails"]
errors.append(f" {exc.get('text','')} at {exc.get('url','')}:{exc.get('lineNumber','')}")
except:
break
if errors:
for e in errors:
print(e)
else:
print(" No JS exceptions detected")
ws.close()
print("\n[DONE]")