fix(extension): restore AI Response Content capture by patching DOM extraction, CSP connect-src, and TS regex literal serialization
This commit is contained in:
144
test_dom_mock.js
Normal file
144
test_dom_mock.js
Normal file
@@ -0,0 +1,144 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const jsdom = require('jsdom');
|
||||
const { JSDOM } = jsdom;
|
||||
|
||||
const observerModule = require('./extension/out/observer-script.js');
|
||||
const observerCode = observerModule.generateApprovalObserverScript(8080);
|
||||
const rawDump = JSON.parse(fs.readFileSync('C:/Users/Variet-Worker/.gemini/antigravity/bridge/dump_html.json', 'utf8'));
|
||||
|
||||
// Instantiate DOM
|
||||
const dom = new JSDOM(rawDump.html, { runScripts: "dangerously", pretendToBeVisual: true, url: "http://localhost/" });
|
||||
const window = dom.window;
|
||||
const document = window.document;
|
||||
|
||||
// Polyfill offsetParent for visibility check
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'offsetParent', {
|
||||
get() { return document.body; }
|
||||
});
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'style', {
|
||||
get() { return { display: 'block' }; }
|
||||
});
|
||||
|
||||
// Mock innerText (JSDOM does not fully support it, but generic walker uses nodeValue / textContent)
|
||||
// Our logic uses nodeValue for TextNodes, so it will work in JSDOM out of the box!
|
||||
|
||||
// Setup the DOM tree to perfectly match a real Chat conversation
|
||||
const titleSpan = document.querySelector('span[title^="command("]');
|
||||
if(!titleSpan) {
|
||||
console.error("COULD NOT FIND command SPAN in dump?!");
|
||||
process.exit(1);
|
||||
}
|
||||
// Find the card container
|
||||
let toolContainer = titleSpan.parentElement;
|
||||
while(toolContainer && !toolContainer.className.includes("border-gray-500/10") && !toolContainer.className.includes("bg-gray-500/10")) {
|
||||
toolContainer = toolContainer.parentElement;
|
||||
}
|
||||
if(!toolContainer) toolContainer = titleSpan.parentElement; // fallback
|
||||
|
||||
// Create an AI text block just above it
|
||||
const aiChat = document.createElement('div');
|
||||
aiChat.className = 'markdown prose';
|
||||
aiChat.innerHTML = '<p>안녕하세요! 시스템을 수정하기 위해 요청하신 작업을 시작합니다. <b>디스코드 릴레이 기능 복구</b>를 위해 스크립트를 실행하겠습니다.</p>';
|
||||
|
||||
// Wrap them up in the turn container
|
||||
const parent = toolContainer.parentElement;
|
||||
const convoWrapper = document.createElement('div');
|
||||
convoWrapper.className = 'bg-agent-convo-background';
|
||||
parent.insertBefore(convoWrapper, toolContainer);
|
||||
convoWrapper.appendChild(aiChat);
|
||||
convoWrapper.appendChild(toolContainer); // Move tool inside the convo wrapper as a sibling to AI chat
|
||||
|
||||
// Add action button to the tool container
|
||||
const btn = document.createElement('button');
|
||||
btn.innerHTML = '<span class="truncate">Allow</span>';
|
||||
toolContainer.appendChild(btn);
|
||||
|
||||
console.log("Mock Button offsetParent:", btn.offsetParent ? btn.offsetParent.tagName : 'null');
|
||||
console.log("Mock Button display:", btn.style.display);
|
||||
console.log("Mock Button text:", btn.textContent);
|
||||
|
||||
// MOCK FETCH
|
||||
const fetchCalls = [];
|
||||
window.fetch = function(url, options) {
|
||||
fetchCalls.push({url, options});
|
||||
if (url.includes('/ping')) {
|
||||
return Promise.resolve({ text: function() { return Promise.resolve('pong'); } });
|
||||
}
|
||||
if (url.includes('/pending')) {
|
||||
return Promise.resolve({ json: function() { return Promise.resolve({ok: true, request_id: 'test-rid'}); } });
|
||||
}
|
||||
return Promise.resolve({ json: function() { return Promise.resolve({}); } });
|
||||
};
|
||||
|
||||
// Polyfill offsetParent for visibility check
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'offsetParent', {
|
||||
get() { return document.body; }
|
||||
});
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'style', {
|
||||
get() { return { display: 'block' }; }
|
||||
});
|
||||
|
||||
const originalLog = console.log;
|
||||
window.console.log = function(...args) {
|
||||
if (args.length > 0 && typeof args[0] === 'string' && args[0].includes("NOT MATCHED")) {
|
||||
let txt = args[1];
|
||||
let codes = [];
|
||||
if (txt) {
|
||||
for(let i=0; i<txt.length; i++) codes.push(txt.charCodeAt(i));
|
||||
}
|
||||
originalLog('[JSDOM-WIN]', args[0], `\nRAW="${txt}"\nCODES=[${codes.join(',')}]`);
|
||||
} else {
|
||||
originalLog('[JSDOM-WIN]', ...args);
|
||||
}
|
||||
};
|
||||
|
||||
// Inject the observer
|
||||
const scriptEl = document.createElement('script');
|
||||
scriptEl.textContent = observerCode;
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
console.log("Observer injected, waiting for cycles...");
|
||||
console.log("Total Buttons in DOM:", document.querySelectorAll('button').length);
|
||||
|
||||
// Emulate UI mutation to trigger the MutationObserver and force an instant scan()
|
||||
setTimeout(() => {
|
||||
console.log("Triggering DOM mutation to force scan()...");
|
||||
document.body.appendChild(document.createElement('span'));
|
||||
}, 1500);
|
||||
|
||||
// Give it time to finish scan().
|
||||
setTimeout(() => {
|
||||
console.log("\n====== FETCH CALLS ======");
|
||||
if(fetchCalls.length === 0) console.log("NO FETCH CALLS MADE!");
|
||||
|
||||
fetchCalls.forEach(c => {
|
||||
console.log(`\n[${c.options ? c.options.method || 'GET' : 'GET'}] ${c.url}`);
|
||||
if(c.options && c.options.body) {
|
||||
try {
|
||||
let j = JSON.parse(c.options.body);
|
||||
console.log("[BODY] request_id:", j.request_id);
|
||||
console.log("[BODY] command:", j.command);
|
||||
console.log("[BODY] description:\n" + "=".repeat(40) + "\n" + j.description + "\n" + "=".repeat(40));
|
||||
} catch(e) {
|
||||
console.log("[BODY]", c.options.body);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Determine success
|
||||
const pendingCall = fetchCalls.find(c => c.url.includes('/pending'));
|
||||
if(pendingCall && pendingCall.options && pendingCall.options.body) {
|
||||
const payload = JSON.parse(pendingCall.options.body);
|
||||
if(payload.description.includes("안녕하세요!") && payload.description.includes("METHOD=TITLE_SPAN")) {
|
||||
console.log("\n✅ SUCCESS: Both Chat Body & Tool Command effectively extracted!");
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log("\n❌ FAIL: Payload description missing either chat text or command string!");
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.log("\n❌ FAIL: /pending never called!");
|
||||
process.exit(1);
|
||||
}
|
||||
}, 4000);
|
||||
Reference in New Issue
Block a user