fix: 移除 props.useSessions(宿主useSyncExternalStore崩溃),改 ctx.sessions.list 订阅

This commit is contained in:
Aisland
2026-08-17 12:10:34 +08:00
parent 981c2acfb3
commit 1d8e969a88
4 changed files with 204 additions and 10 deletions
+66
View File
@@ -0,0 +1,66 @@
const fs = require("fs");
const vm = require("vm");
const code = fs.readFileSync("E:/AI_Workspace/DeepseekHarness/Projects/dsh-worktable/01_content/lib/client.js", "utf8");
function makeEl() {
return {
setAttribute(){}, removeAttribute(){}, appendChild(){}, remove(){}, addEventListener(){},
removeEventListener(){}, classList: { add(){}, remove(){}, contains(){ return false } },
style: {}, dataset: {}, textContent: "", children: [],
};
}
let registered = null;
const moduleLoader = { load(spec){ registered = spec; } };
const sandbox = { self: null,
console, setTimeout, clearTimeout, setInterval, clearInterval,
window: {
__ModuleLoader__: moduleLoader,
addEventListener(){}, removeEventListener(){}, dispatchEvent(){ return true },
innerWidth: 1440, innerHeight: 900, setTimeout, clearTimeout, setInterval, clearInterval,
},
__ModuleLoader__: moduleLoader,
document: {
createElement(){ return makeEl() },
head: { appendChild(){}, removeChild(){} },
body: { appendChild(){} },
querySelector(){ return null }, querySelectorAll(){ return [] },
addEventListener(){}, removeEventListener(){},
},
localStorage: { getItem(){ return null }, setItem(){}, removeItem(){} },
CustomEvent: class { constructor(t, o){ this.type = t; this.detail = o && o.detail } },
ResizeObserver: class { observe(){} disconnect(){} },
MutationObserver: class { observe(){} disconnect(){} },
navigator: {}, location: { protocol: "http:", host: "127.0.0.1:3080" },
fetch: async () => { throw new Error("no fetch") },
WebSocket: class {},
URL: require("url").URL, URLSearchParams: require("url").URLSearchParams,
TextDecoder: require("util").TextDecoder, TextEncoder: require("util").TextEncoder,
performance: { now: () => 0 },
};
sandbox.self = sandbox; try {
vm.createContext(sandbox);
vm.runInContext(code, sandbox, { filename: "client.js" });
console.log("模块作用域执行 OK;已注册:", registered && registered.id);
if (registered) {
const fakeRequire = (name) => {
const stubs = {
"react": { useState: (v)=>[v,()=>{}], useEffect: ()=>{}, useLayoutEffect: ()=>{}, useMemo: (f)=>f(), useCallback: (f)=>f, useRef: (v)=>({current:v}), createElement: ()=>null, Fragment: null },
"react/jsx-runtime": { jsx: ()=>({}) },
"react/jsx-dev-runtime": { jsx: ()=>({}) },
"@deepseek-ai/dsh-client-ui-slots": {},
"@deepseek-ai/dsh-client-ui-primitives": {},
"@deepseek-ai/dsh-client-locale": {},
};
if (name in stubs) return stubs[name];
throw new Error("unexpected require: " + name);
};
sandbox.self = sandbox; try {
const exports = registered.factory(fakeRequire);
console.log("工厂执行 OK, exports keys:", Object.keys(exports || {}));
} catch (e) {
console.log("工厂执行抛错:", e && e.stack ? e.stack.split("\n").slice(0,8).join("\n") : e);
}
}
} catch (e) {
console.log("模块作用域抛错:", e && e.stack ? e.stack.split("\n").slice(0,10).join("\n") : e);
}
+86
View File
@@ -0,0 +1,86 @@
const http = require('http');
const { spawn } = require('child_process');
const fs = require('fs');
const WebSocket = require('ws');
const PORT = 9333;
const candidates = [
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
];
const chrome = candidates.find((p) => fs.existsSync(p));
if (!chrome) { console.log('NO_BROWSER_FOUND'); process.exit(1); }
console.log('browser:', chrome);
const proc = spawn(chrome, [
'--headless=new', '--disable-gpu', '--no-first-run', '--no-default-browser-check',
'--remote-debugging-port=' + PORT,
'--user-data-dir=C:\\Users\\SJL\\AppData\\Local\\Temp\\wt-chrome-debug',
'about:blank',
], { stdio: 'ignore' });
function getJSON(path) {
return new Promise((res, rej) => {
http.get({ host: '127.0.0.1', port: PORT, path }, (r) => {
let d = '';
r.on('data', (c) => d += c);
r.on('end', () => { try { res(JSON.parse(d)) } catch (e) { rej(e) } });
}).on('error', rej);
});
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
(async () => {
let list = null;
for (let i = 0; i < 40; i++) {
try { list = await getJSON('/json/list'); if (list && list.length) break; } catch {}
await sleep(500);
}
if (!list || !list.length) { console.log('NO_DEBUG_TARGET'); proc.kill(); process.exit(1); }
const target = list.find((t) => t.type === 'page') || list[0];
const ws = new WebSocket(target.webSocketDebuggerUrl);
let id = 0;
const pending = new Map();
const events = [];
const send = (method, params) => new Promise((res) => {
const mid = ++id;
pending.set(mid, res);
ws.send(JSON.stringify({ id: mid, method, params }));
});
ws.on('message', (raw) => {
const msg = JSON.parse(String(raw));
if (msg.id && pending.has(msg.id)) { pending.get(msg.id)(msg); pending.delete(msg.id); }
else if (msg.method) events.push(msg);
});
await new Promise((r) => ws.on('open', r));
await send('Runtime.enable');
await send('Log.enable');
await send('Page.enable');
await send('Page.navigate', { url: 'http://127.0.0.1:3080/' });
await sleep(12000);
const result = await send('Runtime.evaluate', {
expression: "JSON.stringify({ wtSection: !!document.querySelector('.dsh-wt_section'), taCard: !!document.querySelector('.ta_card'), prCard: !!document.querySelector('.pr_card'), taEntry: !!document.querySelector('.dsh-ta_entry'), usageDock: !!document.querySelector('.u_dock'), railBox: !!document.querySelector('.dsh-wt_railBox'), readyState: document.readyState, bodyText: (document.body ? document.body.innerText.slice(0,200) : '') })",
returnByValue: true,
});
console.log('DOM_STATE:', result.result && result.result.result && result.result.result.value);
const errors = events.filter((e) =>
e.method === 'Runtime.exceptionThrown' ||
(e.method === 'Log.entryAdded' && (e.params.entry.level === 'error' || e.params.entry.level === 'warning')) ||
(e.method === 'Runtime.consoleAPICalled' && e.params.type === 'error'),
).slice(0, 25).map((e) => {
if (e.method === 'Runtime.exceptionThrown') {
const d = e.params.exceptionDetails;
return 'EXCEPTION: ' + (d.exception && d.exception.description || d.text) + ' @ ' + (d.url || '') + ':' + (d.lineNumber || 0);
}
if (e.method === 'Log.entryAdded') return 'LOG[' + e.params.entry.level + ']: ' + (e.params.entry.text || '').slice(0, 300);
return 'CONSOLE_ERROR: ' + (e.params.args || []).map((a) => a.value || a.description || '').join(' ').slice(0, 300);
});
console.log('ERRORS_COUNT:', errors.length);
errors.forEach((x) => console.log(x));
ws.close();
proc.kill();
process.exit(0);
})().catch((e) => { console.log('SCRIPT_FAIL:', e); proc.kill(); process.exit(1); });