a67b95cbc4
- IoT Provider: 重写Execute()支持多设备命令批量执行,修复persona路径 - Intent Analyzer: 新增isStrongIoTCommand快速通道,跳过LLM分析节省2-3s - Orchestrator: parseReviewMessages()内联审查 + 快速通道扩展(chat/greeting跳过子会话) - Gateway: SSE review_messages解析→WebSocket结构化消息转发(action/chat) - Persona: 对话风格注入action格式指令(括号包裹动作描述) - Frontend: sessionStore历史消息msgType映射 - 新增E2E测试脚本 + 调试标准文档 + 第4轮修复报告 E2E验证: IoT设备操控✅ Review消息拆分✅ 快速通道✅ 响应时间~3.4s Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
180 lines
6.3 KiB
JavaScript
180 lines
6.3 KiB
JavaScript
import { WebSocket } from 'ws';
|
|
import http from 'http';
|
|
|
|
const CDP_HOST = 'localhost';
|
|
const CDP_PORT = 9222;
|
|
|
|
function httpRequest(method, path) {
|
|
return new Promise((resolve, reject) => {
|
|
const req = http.request({ hostname: CDP_HOST, port: CDP_PORT, method, path }, (res) => {
|
|
let data = '';
|
|
res.on('data', (c) => data += c);
|
|
res.on('end', () => {
|
|
try { resolve(JSON.parse(data)); } catch(e) { resolve(data); }
|
|
});
|
|
});
|
|
req.on('error', reject);
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
function cdpCommand(ws, method, params = {}) {
|
|
const id = Math.floor(Math.random() * 1000000);
|
|
return new Promise((resolve) => {
|
|
const handler = (raw) => {
|
|
try {
|
|
const msg = JSON.parse(raw.toString());
|
|
if (msg.id === id) {
|
|
ws.off('message', handler);
|
|
resolve(msg.result || msg);
|
|
}
|
|
} catch {}
|
|
};
|
|
ws.on('message', handler);
|
|
ws.send(JSON.stringify({ id, method, params }));
|
|
setTimeout(() => { ws.off('message', handler); resolve({ error: 'timeout' }); }, 10000);
|
|
});
|
|
}
|
|
|
|
async function main() {
|
|
console.log('[1] Creating new tab and setting fake auth token...');
|
|
const newTab = await httpRequest('PUT', '/json/new?about:blank');
|
|
const ws = new WebSocket(newTab.webSocketDebuggerUrl);
|
|
await new Promise((resolve) => ws.on('open', resolve));
|
|
|
|
await cdpCommand(ws, 'Runtime.enable');
|
|
await cdpCommand(ws, 'Page.enable');
|
|
await cdpCommand(ws, 'Log.enable');
|
|
await cdpCommand(ws, 'Network.enable');
|
|
|
|
// Navigate to app domain first so we can set localStorage
|
|
await cdpCommand(ws, 'Page.navigate', { url: 'http://localhost:5199/' });
|
|
await new Promise(r => setTimeout(r, 2000));
|
|
|
|
// Set fake auth token and user_id in localStorage
|
|
console.log('[2] Injecting fake token into localStorage...');
|
|
const setStorage = await cdpCommand(ws, 'Runtime.evaluate', {
|
|
expression: `
|
|
localStorage.setItem('token', 'fake_test_token_abc123');
|
|
localStorage.setItem('user_id', 'admin_test_user');
|
|
'done'
|
|
`,
|
|
returnByValue: true,
|
|
});
|
|
console.log(' localStorage set:', setStorage?.result?.value);
|
|
|
|
// Now reload the page so auth store picks up the token
|
|
console.log('[3] Reloading page with auth token...');
|
|
|
|
const errors = [];
|
|
const logs = [];
|
|
ws.on('message', (raw) => {
|
|
try {
|
|
const msg = JSON.parse(raw.toString());
|
|
if (msg.method === 'Runtime.exceptionThrown') {
|
|
const ed = msg.params.exceptionDetails;
|
|
const errText = ed.text || ed.exception?.description || '';
|
|
errors.push(errText);
|
|
console.log(` ❌ EXCEPTION: ${errText.substring(0, 300)}`);
|
|
if (ed.exception?.className) console.log(` class: ${ed.exception.className}`);
|
|
if (ed.stackTrace?.callFrames) {
|
|
ed.stackTrace.callFrames.slice(0, 5).forEach(f => {
|
|
console.log(` at ${f.functionName || '(anon)'} (${f.url}:${f.lineNumber}:${f.columnNumber})`);
|
|
});
|
|
}
|
|
}
|
|
if (msg.method === 'Log.entryAdded') {
|
|
const entry = msg.params.entry;
|
|
const text = entry.text?.substring(0, 200) || '';
|
|
console.log(` LOG [${entry.level}]: ${text}`);
|
|
if (entry.level === 'error' || entry.level === 'warning') {
|
|
errors.push(`[LOG:${entry.level}] ${text}`);
|
|
}
|
|
}
|
|
if (msg.method === 'Runtime.consoleAPICalled') {
|
|
const p = msg.params;
|
|
if (p.type === 'error') {
|
|
const text = p.args?.map(a => a.value || a.description || '').join(' ') || '';
|
|
errors.push(`[CONSOLE:${p.type}] ${text}`);
|
|
console.log(` CONSOLE.error: ${text.substring(0, 200)}`);
|
|
} else {
|
|
logs.push(`[${p.type}] ${p.args?.map(a => a.value || '').join(' ').substring(0, 150)}`);
|
|
}
|
|
}
|
|
if (msg.method === 'Network.responseReceived') {
|
|
const resp = msg.params.response;
|
|
if (resp.status >= 400) {
|
|
console.log(` NET ${resp.status}: ${resp.url.substring(0, 150)}`);
|
|
}
|
|
}
|
|
} catch {}
|
|
});
|
|
|
|
await cdpCommand(ws, 'Page.reload', { ignoreCache: true });
|
|
await new Promise(r => setTimeout(r, 8000));
|
|
|
|
console.log('\n[4] Checking DOM state...');
|
|
|
|
// Check #root
|
|
const e1 = await cdpCommand(ws, 'Runtime.evaluate', {
|
|
expression: `
|
|
(() => {
|
|
const root = document.getElementById('root');
|
|
if (!root) return 'NO_ROOT';
|
|
const h = root.innerHTML.trim();
|
|
if (!h) return 'ROOT_EMPTY';
|
|
return 'ROOT_LEN=' + h.length + ' FIRST:' + h.substring(0, 300);
|
|
})()
|
|
`,
|
|
returnByValue: true,
|
|
});
|
|
console.log(' #root:', e1?.result?.value);
|
|
|
|
// Check body
|
|
const e2 = await cdpCommand(ws, 'Runtime.evaluate', {
|
|
expression: `document.body ? 'BODY_OK children=' + document.body.children.length : 'NO_BODY'`,
|
|
returnByValue: true,
|
|
});
|
|
console.log(' body:', e2?.result?.value);
|
|
|
|
// Check if React rendered the chat interface
|
|
const e3 = await cdpCommand(ws, 'Runtime.evaluate', {
|
|
expression: `
|
|
(() => {
|
|
const root = document.getElementById('root');
|
|
const h = root ? root.innerHTML : '';
|
|
const hasAppLayout = h.includes('h-screen');
|
|
const hasSidebar = h.includes('sidebar') || h.includes('新对话');
|
|
const hasHeader = h.includes('昔涟') && h.includes('退出');
|
|
const hasChatContainer = h.includes('ChatContainer') || h.includes('开始一段新对话');
|
|
const hasChatInput = h.includes('和昔涟说点什么');
|
|
return JSON.stringify({ hasAppLayout, hasSidebar, hasHeader, hasChatContainer, hasChatInput, totalLen: h.length });
|
|
})()
|
|
`,
|
|
returnByValue: true,
|
|
});
|
|
console.log(' Features:', e3?.result?.value);
|
|
|
|
// Check for any React ErrorBoundary or error state
|
|
const e4 = await cdpCommand(ws, 'Runtime.evaluate', {
|
|
expression: `
|
|
(() => {
|
|
const body = document.body;
|
|
return body ? body.innerText.substring(0, 500) : 'NO_BODY';
|
|
})()
|
|
`,
|
|
returnByValue: true,
|
|
});
|
|
console.log(' Text:', e4?.result?.value);
|
|
|
|
ws.close();
|
|
|
|
console.log('\n=== SUMMARY ===');
|
|
console.log('Total errors:', errors.length);
|
|
errors.forEach((e, i) => console.log(` ERR${i+1}: ${e.substring(0, 500)}`));
|
|
console.log('Total logs:', logs.length);
|
|
logs.forEach(l => console.log(' LOG:', l.substring(0, 200)));
|
|
}
|
|
|
|
main().catch(e => { console.error('Fatal:', e.message); process.exit(1); });
|