a058b0ab8e
- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复) - 修复IoT子会话工具调用链路日志缺失 - 新增最终审查子会话(review_provider) 支持消息格式解析拆分 - 实现历史消息持久化(后端存储+前端分页加载) - 前端新增动作消息(ActionMessage)类型和渲染 - 优化对话链路速度(非阻塞子会话+快速问候通道) - JWT密钥环境变量化(无默认值启动panic) - Token自动刷新机制(401拦截器+refresh接口) - WebSocket指数退避重连(jitter+最大10次) - localStorage清理一致性(cyrene_前缀+版本检查) - IoT环境变量统一为IOT_SERVICE_URL
199 lines
7.2 KiB
Python
199 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
"""CDP v3: 深度诊断前端白屏问题 — 检查 DOM、网络、JS 模块加载"""
|
|
import json, time, base64, urllib.request, os
|
|
|
|
# Step 1: 打开新页面
|
|
print("=== Step 1: 导航到前端页面 ===")
|
|
req = urllib.request.Request("http://127.0.0.1:9225/json/new?url=http://localhost:5199/", method="PUT")
|
|
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}")
|
|
print(f" WS URL: {ws_url[:80]}")
|
|
|
|
from websocket import create_connection
|
|
ws = create_connection(ws_url, timeout=10)
|
|
|
|
def cdp(method, params=None, msg_id=1):
|
|
ws.send(json.dumps({"id": msg_id, "method": method, "params": params or {}}))
|
|
|
|
def recv_all(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 2: 启用所有 domains
|
|
print("\n=== Step 2: 启用所有 domains ===")
|
|
cdp("Page.enable", {}, 1)
|
|
cdp("Network.enable", {}, 2)
|
|
cdp("Runtime.enable", {}, 3)
|
|
cdp("Log.enable", {}, 4)
|
|
cdp("DOM.enable", {}, 5)
|
|
recv_all(1)
|
|
|
|
# Step 3: 重新 navigate 以捕获网络事件
|
|
print("\n=== Step 3: 重新导航 ===")
|
|
cdp("Page.navigate", {"url": "http://localhost:5199/"}, 10)
|
|
time.sleep(3)
|
|
|
|
# 收集在此期间的所有消息
|
|
print(" 收集网络/运行时事件...")
|
|
all_msgs = recv_all(5)
|
|
|
|
# 分析网络事件
|
|
print("\n=== Step 4: 网络请求分析 ===")
|
|
requests = {}
|
|
for m in all_msgs:
|
|
try:
|
|
d = json.loads(m)
|
|
except:
|
|
continue
|
|
method = d.get("method","")
|
|
params = d.get("params",{})
|
|
|
|
if method == "Network.requestWillBeSent":
|
|
req_info = params.get("request", {})
|
|
url = req_info.get("url","")
|
|
req_id = params.get("requestId","")
|
|
rtype = params.get("type","")
|
|
requests[req_id] = {"url": url, "type": rtype, "status": "pending"}
|
|
|
|
elif method == "Network.responseReceived":
|
|
req_id = params.get("requestId","")
|
|
resp = params.get("response",{})
|
|
status = resp.get("status",0)
|
|
mime = resp.get("mimeType","")
|
|
if req_id in requests:
|
|
requests[req_id]["status"] = status
|
|
requests[req_id]["mime"] = mime
|
|
|
|
elif method == "Network.loadingFailed":
|
|
req_id = params.get("requestId","")
|
|
err = params.get("errorText","")
|
|
if req_id in requests:
|
|
requests[req_id]["status"] = "FAILED"
|
|
requests[req_id]["error"] = err
|
|
|
|
elif method == "Runtime.exceptionThrown":
|
|
exc = params.get("exceptionDetails",{})
|
|
print(f" [EXCEPTION] {exc.get('text','')} at {exc.get('url','')}:{exc.get('lineNumber','')}")
|
|
|
|
elif method == "Log.entryAdded":
|
|
entry = params.get("entry",{})
|
|
print(f" [CONSOLE:{entry.get('level','log')}] {entry.get('text','')[:200]}")
|
|
|
|
elif method == "Runtime.consoleAPICalled":
|
|
args = params.get("args",[])
|
|
msg_type = params.get("type","log")
|
|
texts = []
|
|
for a in args:
|
|
t = a.get("value","") or a.get("description","")
|
|
texts.append(str(t)[:150])
|
|
print(f" [CONSOLE:{msg_type}] {' '.join(texts)}")
|
|
|
|
print(f"\n Total network requests: {len(requests)}")
|
|
for rid, info in requests.items():
|
|
status_str = str(info.get("status","?"))
|
|
type_str = info.get("type","?")
|
|
url_short = info.get("url","")[-80:]
|
|
error_str = f" ERROR={info.get('error','')}" if info.get("error") else ""
|
|
print(f" [{type_str}] {status_str} {url_short}{error_str}")
|
|
|
|
# Step 5: 运行时评估 - 深度检查
|
|
print("\n=== Step 5: 运行时深度评估 ===")
|
|
checks = [
|
|
("window.location.href", "window.location.href"),
|
|
("document.readyState", "document.readyState"),
|
|
("document.title", "document.title"),
|
|
("root element", "document.getElementById('root') ? 'EXISTS' : 'NULL'"),
|
|
("body innerHTML length", "document.body ? document.body.innerHTML.length : -1"),
|
|
("body children count", "document.body ? document.body.children.length : -1"),
|
|
("head children count", "document.head ? document.head.children.length : -1"),
|
|
("all scripts", "Array.from(document.querySelectorAll('script')).map(s => ({src:s.src,type:s.type,async:s.async,defer:s.defer})).slice(0,5)"),
|
|
("React DOM check", "typeof React !== 'undefined' ? 'React global found' : (document.querySelector('#root') ? 'root exists but no React global' : 'root missing')"),
|
|
("SW registration check", "'serviceWorker' in navigator ? 'SW API available' : 'NO SW API'"),
|
|
("localStorage token", "localStorage.getItem('token') || 'no token'"),
|
|
]
|
|
|
|
for idx, (label, expr) in enumerate(checks):
|
|
msg_id = 100 + idx
|
|
cdp("Runtime.evaluate", {"expression": expr, "returnByValue": True}, msg_id)
|
|
recv_msgs = recv_all(2)
|
|
r = find_result(recv_msgs, msg_id)
|
|
|
|
if r:
|
|
val = r.get("result",{}).get("value","?")
|
|
# 处理 object 类型
|
|
if isinstance(val, dict) and "objectId" in r.get("result",{}):
|
|
val = r["result"].get("description","[object]")
|
|
err = r.get("result",{}).get("description","") or ""
|
|
print(f" {label}: {val}")
|
|
if r.get("result",{}).get("type") == "object":
|
|
# Try to get properties
|
|
obj_id = r["result"].get("objectId","")
|
|
if obj_id:
|
|
cdp("Runtime.getProperties", {"objectId": obj_id, "ownProperties": True}, 900 + idx)
|
|
prop_msgs = recv_all(2)
|
|
prop_r = find_result(prop_msgs, 900 + idx)
|
|
if prop_r:
|
|
for prop in prop_r.get("result",[]):
|
|
v = prop.get("value",{})
|
|
print(f" .{prop.get('name','?')} = {v.get('value', v.get('description','?'))}")
|
|
else:
|
|
print(f" {label}: NO RESULT")
|
|
|
|
# Step 6: 截图
|
|
print("\n=== Step 6: 截图 ===")
|
|
cdp("Page.captureScreenshot", {"format": "png", "captureBeyondViewport": True}, 20)
|
|
recv_msgs = recv_all(5)
|
|
r = find_result(recv_msgs, 20)
|
|
if r and r.get("data"):
|
|
img = base64.b64decode(r["data"])
|
|
fp = "/tmp/cyrene_screenshot_round12_v3.png"
|
|
with open(fp, "wb") as f:
|
|
f.write(img)
|
|
print(f" Screenshot: {len(img)} bytes -> {fp}")
|
|
else:
|
|
print(f" Screenshot failed: {str(r)[:200]}")
|
|
|
|
# Step 7: 检查是否有 JS 异常
|
|
print("\n=== Step 7: 最终 JS 异常检查 ===")
|
|
ws.settimeout(1)
|
|
has_exception = False
|
|
for i in range(5):
|
|
try:
|
|
d = json.loads(ws.recv())
|
|
if d.get("method") == "Runtime.exceptionThrown":
|
|
exc = d["params"]["exceptionDetails"]
|
|
print(f" [EXCEPTION] text={exc.get('text','')}")
|
|
print(f" url={exc.get('url','')}")
|
|
print(f" line={exc.get('lineNumber','')} col={exc.get('columnNumber','')}")
|
|
stack = exc.get("stackTrace",{})
|
|
for frame in stack.get("callFrames",[]):
|
|
print(f" at {frame.get('functionName','')} {frame.get('url','')}:{frame.get('lineNumber','')}")
|
|
has_exception = True
|
|
except:
|
|
break
|
|
|
|
if not has_exception:
|
|
print(" No exceptions caught")
|
|
|
|
ws.close()
|
|
print("\n[DONE v3]")
|