Files
Cyrene/debug/diagnose_final.mjs
AskaEth a058b0ab8e fix: 第一轮修复 - 记忆管理/IoT操控/历史消息持久化/动作消息/链路优化/安全配置
- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复)
- 修复IoT子会话工具调用链路日志缺失
- 新增最终审查子会话(review_provider) 支持消息格式解析拆分
- 实现历史消息持久化(后端存储+前端分页加载)
- 前端新增动作消息(ActionMessage)类型和渲染
- 优化对话链路速度(非阻塞子会话+快速问候通道)
- JWT密钥环境变量化(无默认值启动panic)
- Token自动刷新机制(401拦截器+refresh接口)
- WebSocket指数退避重连(jitter+最大10次)
- localStorage清理一致性(cyrene_前缀+版本检查)
- IoT环境变量统一为IOT_SERVICE_URL
2026-05-21 23:10:07 +08:00

180 lines
6.7 KiB
JavaScript

#!/usr/bin/env node
/**
* 最终诊断 — 检查 JS 控制台错误 + 终端渲染确认
*/
import { writeFileSync } from 'fs';
import WebSocket from 'ws';
const CDP_PORT = 9225;
const BASE = 'http://localhost:5199';
const API = 'http://localhost:8080/api/v1';
const OUT = '/home/aska/Code/Cyrene/debug/logs/chromium';
function makeCDPHelper(ws) {
let msgId = 1;
const pending = new Map();
ws.on('message', (raw) => {
try {
const msg = JSON.parse(raw.toString());
if (msg.id && pending.has(msg.id)) {
pending.get(msg.id)(msg.result || msg);
pending.delete(msg.id);
}
} catch {}
});
return (method, params = {}) => new Promise((resolve, reject) => {
const id = msgId++;
const timer = setTimeout(() => { pending.delete(id); reject(new Error('CDP timeout: ' + method)); }, 15000);
ws.send(JSON.stringify({ id, method, params }));
pending.set(id, (r) => { clearTimeout(timer); resolve(r); });
});
}
async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
async function main() {
// Login
const loginRes = await fetch(`${API}/auth/login`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'admin123' }),
});
const { token, user_id: userId } = await loginRes.json();
if (!token) throw new Error('Login failed');
// Use existing page
const pages = await (await fetch(`http://127.0.0.1:${CDP_PORT}/json`)).json();
const ourPage = pages.find(p => p.url === BASE || p.url === `${BASE}/`);
if (!ourPage) throw new Error('No page found');
// Connect CDP
const pw = new WebSocket(ourPage.webSocketDebuggerUrl);
await new Promise((resolve, reject) => {
pw.once('open', resolve); pw.once('error', reject);
setTimeout(() => reject(new Error('page WS timeout')), 5000);
});
const cdp = makeCDPHelper(pw);
await cdp('Page.enable');
await cdp('Runtime.enable');
await cdp('Log.enable');
// Collect console messages
const consoleMessages = [];
pw.on('message', (raw) => {
try {
const msg = JSON.parse(raw.toString());
if (msg.method === 'Runtime.consoleAPICalled') {
consoleMessages.push({
type: msg.params.type,
text: msg.params.args?.map(a => a.value ?? a.description ?? '').join(' ').slice(0, 300),
});
}
if (msg.method === 'Log.entryAdded') {
consoleMessages.push({
type: 'log',
text: msg.params.entry?.text?.slice(0, 300) || '',
});
}
} catch {}
});
// Inject auth, reload
await cdp('Runtime.evaluate', { expression: `localStorage.setItem('token', ${JSON.stringify(token)}); localStorage.setItem('user_id', '${userId}');` });
await cdp('Page.navigate', { url: BASE });
await sleep(5000);
// Get JS errors
await cdp('Runtime.evaluate', {
expression: `window.__errors = []; window.onerror = function(m,s,l,c,e) { window.__errors.push({msg:m,source:s,line:l,col:c,error:String(e)}); }; 'listening';`
});
// Screenshot
console.log('[1] Taking screenshot...');
const ss = await cdp('Page.captureScreenshot', { format: 'png' });
if (ss?.data) {
writeFileSync(`${OUT}/screenshot_layout.png`, Buffer.from(ss.data, 'base64'));
console.log(' ✅ Saved', ss.data.length, 'bytes base64');
}
// Get console errors
const jsErrors = await cdp('Runtime.evaluate', {
returnByValue: true,
expression: `JSON.stringify(window.__errors || [])`
});
console.log('\n[2] JS Errors:', jsErrors?.result?.value || 'none');
// Console messages
console.log('\n[3] Console messages collected:', consoleMessages.length);
for (const m of consoleMessages) {
console.log(` [${m.type}] ${m.text}`);
}
// Check computed styles for rendering issues
console.log('\n[4] Final rendering check...');
const finalCheck = await cdp('Runtime.evaluate', {
returnByValue: true,
expression: `
JSON.stringify({
chatInput: (() => {
const ta = document.querySelector('textarea');
if (!ta) return 'NO_TEXTAREA';
const r = ta.getBoundingClientRect();
// Walk up to find the ChatInput wrapper
let parent = ta.parentElement;
for (let i = 0; i < 10 && parent; i++) {
if (parent.className?.includes?.('border-t')) break;
parent = parent.parentElement;
}
const wrapper = parent;
return {
textareaRect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height), w: Math.round(r.width) },
textareaVisible: r.top < innerHeight && r.bottom > 0 && r.width > 0,
wrapperRect: wrapper ? (() => { const wr = wrapper.getBoundingClientRect(); return { t: Math.round(wr.top), b: Math.round(wr.bottom), h: Math.round(wr.height) }; })() : null,
wrapperBg: wrapper ? getComputedStyle(wrapper).backgroundColor : 'N/A',
wrapperDisplay: wrapper ? getComputedStyle(wrapper).display : 'N/A',
};
})(),
iotStatusBar: (() => {
const cb = document.querySelector('.chat-background');
if (!cb) return 'NO_CHAT_BG';
const kids = Array.from(cb.children);
const iotKid = kids[kids.length - 1]; // Last child of chat-background
if (!iotKid) return 'NO_IOT_KID';
const r = iotKid.getBoundingClientRect();
return {
rect: { t: Math.round(r.top), b: Math.round(r.bottom), h: Math.round(r.height) },
visible: r.top < innerHeight && r.bottom > 0 && r.height > 0,
cls: iotKid.className?.slice?.(0, 200) || '',
cs: { display: getComputedStyle(iotKid).display, bg: getComputedStyle(iotKid).backgroundColor },
txt: (iotKid.textContent || '').slice(0, 100),
};
})(),
overlays: (() => {
const all = Array.from(document.querySelectorAll('*'));
return all.filter(el => {
const cs = getComputedStyle(el);
return (cs.position === 'fixed' || cs.position === 'absolute') &&
parseInt(cs.zIndex) > 0 &&
el.clientHeight > 100;
}).map(el => {
const r = el.getBoundingClientRect();
return {
tag: el.tagName,
cls: (el.className?.slice?.(0, 150) || ''),
rect: { t: Math.round(r.top), b: Math.round(r.bottom), l: Math.round(r.left), r: Math.round(r.right), h: Math.round(r.height) },
zIndex: getComputedStyle(el).zIndex,
position: getComputedStyle(el).position,
};
});
})(),
})
`
});
console.log(finalCheck?.result?.value || 'ERROR');
pw.close();
console.log('\n✅ Done.');
}
main().catch(err => { console.error('Fatal:', err); process.exit(1); });