a058b0ab8e
- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复) - 修复IoT子会话工具调用链路日志缺失 - 新增最终审查子会话(review_provider) 支持消息格式解析拆分 - 实现历史消息持久化(后端存储+前端分页加载) - 前端新增动作消息(ActionMessage)类型和渲染 - 优化对话链路速度(非阻塞子会话+快速问候通道) - JWT密钥环境变量化(无默认值启动panic) - Token自动刷新机制(401拦截器+refresh接口) - WebSocket指数退避重连(jitter+最大10次) - localStorage清理一致性(cyrene_前缀+版本检查) - IoT环境变量统一为IOT_SERVICE_URL
180 lines
6.7 KiB
JavaScript
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); });
|